diff --git a/admin.html b/admin.html index 9dde665..ca0dd56 100644 --- a/admin.html +++ b/admin.html @@ -102,6 +102,7 @@
+ Punkte fĂŒr den Online-Meeting-Ablauf (z.B. BegrĂŒĂung, DSGVO, BNI-Zahlen, Events) â als Bild oder als Live-Einbettung + aus Zoho Show. Die Reihenfolge hier ist nur der Katalog; welche Punkte in welcher Reihenfolge live gezeigt werden, + stellt die Moderatorin frei im Steuerpult (index.html) ein. +
+ +
+ Redezeit anpassen
+Standard-Redezeit fĂŒr alle Mitglieder oder abweichende Zeit pro Person setzen.
+ +A) Standardzeit fĂŒr alle
+Oben in der Kopfzeile des Steuerungs-Fensters auf das Zeit-Feld ââ± 0:50" (bzw. den aktuellen Wert) klicken.
Im Dialog die neue Zeit im Format M:SS (z. B. 1:00) oder als Minuten (z. B. 1) eingeben.
Mit OK bestĂ€tigen â die neue Zeit gilt sofort fĂŒr alle Mitglieder ohne individuelle Zeit.
B) Individuelle Zeit pro Mitglied
+In der Mitgliederliste rechts neben jedem Namen erscheint ein kleines rot umrandetes Zeit-Feld.
Auf dieses Feld klicken und den abweichenden Wert eingeben (leer lassen = Standardzeit ĂŒbernehmen).
NĂŒtzlich z. B. fĂŒr neue Mitglieder mit lĂ€ngerer Vorstellung oder fĂŒr EhrengĂ€ste.
Tab âBesucher" öffnen.
Auf â+ Besucher hinzufĂŒgen" klicken.
Name und optional Firma eingeben und speichern.
Besucher erscheinen am Ende der Vorstellungsrunde nach allen Mitgliedern.
In der rechten Seitenleiste den Tab âBesucher" öffnen.
Name eingeben, optional Firma, Fachgebiet, âEingeladen von" (Mitglied wĂ€hlen) und Foto-URL ergĂ€nzen.
Auf â+ Besucher hinzufĂŒgen" klicken â der Besucher erscheint sofort in der Reihenfolge.
Besucher werden am Ende der Vorstellungsrunde nach allen Mitgliedern eingeblendet.
+
http://localhost:8181/admin.html â am einfachsten ĂŒber den Link im Steuerungs-Fenster unter dem Tab âAdmin".
+ Das Admin-Panel öffnest du am einfachsten ĂŒber den Link im Steuerungs-Fenster unter dem Tab âAdmin" (Button ââ Admin-Panel öffnen") â das funktioniert unabhĂ€ngig davon, wie das Tool gestartet wurde. Alternativ, falls das Tool ĂŒber start.command (Terminal) gestartet wurde, ist es auch direkt unter http://localhost:8181/admin.html erreichbar â diese Adresse funktioniert nur, wenn das Tool auf diesem Weg lĂ€uft.
Im Steuerungs-Fenster den Tab âAdmin" (rechte Seitenleiste) öffnen.
Im Abschnitt â8-Augen-GesprĂ€che" auf âđ„ 8-Augen Timer starten" klicken â der Vollbild-Timer öffnet sich.
In der unteren Leiste unter âWechsel 4er" die gewĂŒnschte Sprechzeit eingeben (z. B. 8:00 fĂŒr 8 Minuten) und mit â ĂŒbernehmen.
Mit den TischgröĂen-Buttons (3er / 4er / 5er / 6er) auswĂ€hlen, welche Timer angezeigt werden sollen â nicht benötigte einfach ausblenden.
Auf ââ¶ Start" klicken â alle sichtbaren Timer laufen gleichzeitig los.
Am Ende jeder Sprechzeit erklingt der Glockenton, danach folgt eine 3 Sekunden Wechsel-Phase (blaue Karte, Countdown), bevor die nÀchste Sprechzeit startet.
Wenn alle Redner durch sind, stoppt der Timer automatisch â die Karten werden grĂŒn mit â Alle N Redner durch.
Mit ââ Beenden" schlieĂt sich der Vollbild-Timer und die App springt zurĂŒck in die Vorstellungsrunde.
+ Ablauf
+Im Steuerungs-Fenster oben in der Kopfzeile auf âđ Mitglieder aktualisieren" klicken.
Schritt 1 (einmalig): Beide Bookmarklet-Buttons in die Lesezeichenleiste des Browsers ziehen. Zuerst âDiagnose" auf der BNI-Mitgliederliste ausfĂŒhren und die heruntergeladene Diagnose-Datei hier hochladen â damit passt sich der Export-Button automatisch an die aktuelle Seitenstruktur an.
Schritt 2: BNI-Mitgliederliste öffnen (Link im Dialog), warten bis alle Mitglieder geladen sind, dann Bookmarklet âBNI Mitglieder exportieren" klicken â es lĂ€dt automatisch eine JSON-Datei herunter.
Schritt 3: Die JSON-Datei per âJSON-Datei laden & synchronisieren" in der App einlesen. Bestehende Mitglieder werden aktualisiert (Foto, Fachgebiet), neue kommen dazu. Es wird niemand gelöscht.
+ Mitgliederdaten (JSON)
+Im Steuerungs-Fenster oben auf â⏠Import / Export" klicken.
âExportieren" â die kompletten Mitgliederdaten werden als Text im Feld angezeigt (per Copy&Paste sichern oder in eine Datei speichern).
âImportieren" â vorher exportierten Text wieder einfĂŒgen und einlesen. Ersetzt die aktuellen Daten im Browser.
Profildaten (Kurzlinks & Offline-Fotos)
+Kurzlinks (z. B. Website-Weiterleitungen) und lokal hochgeladene Fotos werden getrennt gespeichert und bleiben bei einem Mitglieder-Sync erhalten.
â⏠profildaten.json speichern" â Datei sichern und im Projektordner ablegen.
Nach einem Browser-Reset oder auf einem neuen GerĂ€t: â⏠PROFILDATEN.JSON LADEN" â Kurzlinks & Fotos sind wieder da.
+ Punkte im Admin-Panel anlegen
+Im Admin-Panel den Tab âAgenda" öffnen.
Auf â+ Agenda-Punkt hinzufĂŒgen" klicken (oder einen bestehenden Punkt anklicken, um ihn zu bearbeiten).
Eine Bezeichnung eingeben und die Anzeigeart wĂ€hlen: âBild" oder âLive-Einbettung".
Auf âSpeichern" klicken â der Punkt erscheint in der Liste.
+ A) Anzeigeart âBild"
+FĂŒr einen Folien-Export (z. B. aus PowerPoint oder Zoho Show als Bild exportiert). Ăber âBild wĂ€hlen" die Datei aus dem Finder wĂ€hlen â sie wird automatisch auf eine fĂŒr Vollbild-Anzeige passende GröĂe verkleinert, bevor sie hochgeladen wird.
+
+ B) Anzeigeart âLive-Einbettung"
+FĂŒr eine Folie, die das Team gemeinsam in Zoho Show pflegt â Ănderungen sind sofort live, ohne erneuten Upload. In Zoho Show: Datei â Freigeben/Veröffentlichen â Einbetten, den dortigen Link in das Feld âEinbettungs-URL" einfĂŒgen. Am zuverlĂ€ssigsten funktioniert das, wenn jeder Agenda-Punkt eine eigene, kleine Zoho-Show-Datei ist (statt einer groĂen Gesamt-PrĂ€sentation) â dann startet die Einbettung immer bei der ersten Folie.
+
+ Reihenfolge festlegen und live anzeigen
+Im Steuerpult den neuen Tab âAgenda" öffnen (ganz links, neben âMitglieder" und âBesucher").
Punkte per Drag & Drop in die gewĂŒnschte Reihenfolge bringen â genau wie bei der Mitgliederliste.
Auf ââ¶ Anzeigen" klicken, um einen Punkt sofort auf dem Display zu zeigen.
Mit dem đ«-Button lĂ€sst sich ein Punkt aus der aktiven Reihenfolge ausblenden (fĂŒr dieses Meeting nicht gebraucht) â er taucht dann unten unter âAusgeblendet" auf und kann dort per â+ Einblenden" jederzeit wieder aktiviert werden.
+ Marketing-/Ergebnisrunde
+LĂ€uft dieselbe Mitgliederliste noch einmal in derselben Reihenfolge durch wie die Vorstellungsrunde â aber ohne Redezeit-Timer. Gedacht fĂŒr die Runde, in der jedes Mitglied kurz berichtet, was es in der vergangenen Woche fĂŒr andere Mitglieder getan hat (Empfehlungen, Besucher, 4-Augen-GesprĂ€che, Trainings, Umsatzdank).
+âWeiter"-Button folgt der Agenda
+Der rote âWeiter"-Button oben fĂŒhrt automatisch zum jeweils nĂ€chsten Punkt in der festgelegten Reihenfolge â egal ob als NĂ€chstes eine Folie, die Mitglieder-Vorstellungsrunde oder die Besucherrunde kommt. Bei einer Live-Einbettung kann âWeiter" nicht innerhalb der eingebetteten Folie selbst blĂ€ttern (das bleibt Sache von Zoho Show), sondern springt direkt zum nĂ€chsten Agenda-Punkt.
+
+
@@ -1317,6 +1405,7 @@
/agenda.json
+ agendaOrder: [], // Reihenfolge von IDs (Katalog-Punkte + eingebaute Vorstellungsrunden), aktiv fĂŒr dieses Meeting (wie meetingOrder)
+ agendaKnownIds: [], // IDs, die schon einmal automatisch in agendaOrder eingefĂŒgt wurden (verhindert, dass bewusst ausgeblendete Punkte zurĂŒckkehren)
+ currentAgendaItemId: null,
+ marketingActive: false, // true = aktuelle 'members'-Runde ist die Marketing-/Ergebnisrunde (gleiche Reihenfolge, ohne Timer)
+ augen: { // 8-Augen-GesprÀche Multi-Timer
+ visibleSizes: [3, 4, 5, 6],
+ baseSeconds: 600, // Wechsel-Intervall am 4er-Tisch (Basis)
+ startedAt: null,
+ running: false,
+ },
};
}
@@ -2222,6 +2351,18 @@ function renderCurrentView() {
renderControl();
}
_refreshSoundBtn();
+
+ // Augen-Timer-Overlay auch in Control-View sichtbar machen
+ const augenEl = document.getElementById('display-augen-timer');
+ if (augenEl) {
+ if (state.mode === 'augen-timer') {
+ augenEl.style.display = 'flex';
+ renderAugenTimer();
+ _augenStartTick();
+ } else if (view === 'control') {
+ augenEl.style.display = 'none';
+ }
+ }
}
window.addEventListener('hashchange', renderCurrentView);
@@ -2255,6 +2396,31 @@ function renderDisplay() {
const displayHeader = document.querySelector('#view-display .display-header');
const absentSummaryEl = document.getElementById('display-absent-summary');
const coordReportEl = document.getElementById('display-coordinator-report');
+ const augenEl = document.getElementById('display-augen-timer');
+ const agendaItemEl = document.getElementById('display-agenda-item');
+
+ // 8-AUGEN TIMER MODE
+ if (state.mode === 'augen-timer') {
+ emptyEl.style.display = 'none';
+ mainCard.style.display = 'none';
+ contentEl.style.display = 'none';
+ nextBar.style.display = 'none';
+ nextBarEnd.style.display = 'none';
+ roundBadge.classList.remove('visible');
+ progressText.textContent = '';
+ if (displayHeader) displayHeader.style.display = 'none';
+ if (absentSummaryEl) absentSummaryEl.style.display = 'none';
+ if (coordReportEl) coordReportEl.style.display = 'none';
+ if (agendaItemEl) agendaItemEl.style.display = 'none';
+ const qrWrap = document.getElementById('qr-wrap');
+ if (qrWrap) qrWrap.style.display = 'none';
+ if (augenEl) {
+ augenEl.style.display = 'flex';
+ renderAugenTimer();
+ }
+ return;
+ }
+ if (augenEl) augenEl.style.display = 'none';
// COORDINATOR REPORT MODE
if (state.mode === 'coordinator-report') {
@@ -2267,6 +2433,7 @@ function renderDisplay() {
progressText.textContent = '';
if (displayHeader) displayHeader.style.display = '';
if (absentSummaryEl) absentSummaryEl.style.display = 'none';
+ if (agendaItemEl) agendaItemEl.style.display = 'none';
const qrWrap = document.getElementById('qr-wrap');
if (qrWrap) qrWrap.style.display = 'none';
if (coordReportEl) {
@@ -2277,6 +2444,51 @@ function renderDisplay() {
}
if (coordReportEl) coordReportEl.style.display = 'none';
+ // AGENDA-FOLIE MODE (Bild oder Live-Einbettung)
+ if (state.mode === 'agenda-item') {
+ emptyEl.style.display = 'none';
+ mainCard.style.display = 'none';
+ contentEl.style.display = 'none';
+ nextBar.style.display = 'none';
+ nextBarEnd.style.display = 'none';
+ roundBadge.classList.remove('visible');
+ progressText.textContent = '';
+ if (displayHeader) displayHeader.style.display = 'none';
+ if (absentSummaryEl) absentSummaryEl.style.display = 'none';
+ const qrWrap = document.getElementById('qr-wrap');
+ if (qrWrap) qrWrap.style.display = 'none';
+ const it = (state.agendaCatalog || []).find(a => a.id === state.currentAgendaItemId);
+ const imgEl = document.getElementById('agenda-item-image');
+ const embedEl = document.getElementById('agenda-item-embed');
+ const errEl = document.getElementById('agenda-item-error');
+ if (agendaItemEl && it) {
+ agendaItemEl.style.display = 'flex';
+ if (errEl) errEl.style.display = 'none';
+ if (it.type === 'embed' && it.embedUrl) {
+ embedEl.src = it.embedUrl;
+ embedEl.style.display = '';
+ imgEl.style.display = 'none';
+ imgEl.src = '';
+ } else if (it.imagePath) {
+ const src = it.imagePath.startsWith('http') ? it.imagePath
+ : giteaUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
+ imgEl.onerror = () => {
+ imgEl.style.display = 'none';
+ if (errEl) errEl.style.display = 'block';
+ };
+ imgEl.onload = () => { if (errEl) errEl.style.display = 'none'; };
+ imgEl.src = src;
+ imgEl.style.display = '';
+ embedEl.style.display = 'none';
+ embedEl.src = '';
+ }
+ } else if (agendaItemEl) {
+ agendaItemEl.style.display = 'none';
+ }
+ return;
+ }
+ if (agendaItemEl) agendaItemEl.style.display = 'none';
+
// ABSENT SUMMARY MODE
if (state.mode === 'absent-summary') {
emptyEl.style.display = 'none';
@@ -2378,6 +2590,10 @@ function renderDisplay() {
// Round badge
if (state.mode === 'visitors') {
+ roundBadge.textContent = '2. Runde â Besucher';
+ roundBadge.classList.add('visible');
+ } else if (state.mode === 'members' && state.marketingActive) {
+ roundBadge.textContent = 'Marketing-/Ergebnisrunde';
roundBadge.classList.add('visible');
} else {
roundBadge.classList.remove('visible');
@@ -2721,6 +2937,7 @@ function getDisplayData(presenter) {
function renderControl() {
renderMemberQueue();
renderVisitorQueue();
+ renderAgendaQueue();
renderVisitorsList();
renderMembersManage();
renderSubstitutesList();
@@ -2907,6 +3124,287 @@ function renderVisitorQueue() {
});
}
+// ============================================================
+// AGENDA-FOLIEN (Online-Meeting-Ablauf: Bild, Live-Einbettung, oder eingebaute Vorstellungsrunden)
+// ============================================================
+// Die Vorstellungsrunden sind fest eingebaute Agenda-Punkte, damit sie in derselben
+// frei sortierbaren Reihenfolge stehen können wie die Bild-/Einbettungs-Folien.
+const BUILTIN_AGENDA_ITEMS = [
+ { id: '__members_round__', label: 'Mitglieder-Vorstellungsrunde', builtin: 'members' },
+ { id: '__visitors_round__', label: 'Besucher-Vorstellungsrunde', builtin: 'visitors' },
+ { id: '__marketing_round__', label: 'Marketing-/Ergebnisrunde', builtin: 'marketing' },
+];
+
+function getAgendaItem(id) {
+ return BUILTIN_AGENDA_ITEMS.find(b => b.id === id) || (state.agendaCatalog || []).find(a => a.id === id);
+}
+
+// FĂŒgt neu bekannt gewordene Punkte (Katalog + eingebaute Runden) genau einmal automatisch
+// in die aktive Reihenfolge ein. Punkte, die die Moderatorin bewusst ausgeblendet hat,
+// bleiben dauerhaft ausgeblendet (agendaKnownIds merkt sich, was schon einmal eingefĂŒgt wurde).
+function ensureAgendaOrderDefaults() {
+ if (!state.agendaOrder) state.agendaOrder = [];
+ if (!state.agendaKnownIds) state.agendaKnownIds = [];
+ const known = new Set(state.agendaKnownIds);
+ const inOrder = new Set(state.agendaOrder);
+ const allIds = [...BUILTIN_AGENDA_ITEMS.map(b => b.id), ...(state.agendaCatalog || []).map(a => a.id)];
+ allIds.forEach(id => {
+ if (!known.has(id)) {
+ // FĂŒr bereits vorhandene agendaOrder-EintrĂ€ge (Migration von vor agendaKnownIds)
+ // nur als "bekannt" nachtragen, nicht erneut einfĂŒgen.
+ if (!inOrder.has(id)) { state.agendaOrder.push(id); inOrder.add(id); }
+ state.agendaKnownIds.push(id);
+ known.add(id);
+ }
+ });
+ // Vom Server gelöschte Katalog-Punkte aus der aktiven Reihenfolge entfernen; eingebaute Runden bleiben immer gĂŒltig.
+ // Set-Dedupe als Sicherheitsnetz gegen doppelte EintrĂ€ge aus frĂŒheren LĂ€ufen.
+ const validIds = new Set(allIds);
+ state.agendaOrder = Array.from(new Set(state.agendaOrder.filter(id => validIds.has(id))));
+}
+
+function updateAgendaTabVisibility() {
+ const tab = document.getElementById('tab-agenda');
+ if (!tab) return;
+ tab.style.display = ''; // immer sichtbar â enthĂ€lt immer mind. die eingebauten Vorstellungsrunden
+}
+
+async function fetchChapterAgendaFromGitea() {
+ if (!activeChapter) return;
+ if (!state.agendaCatalog) state.agendaCatalog = [];
+ ensureAgendaOrderDefaults();
+ try {
+ const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
+ const items = res.ok ? (await res.json()).items : null;
+ if (Array.isArray(items)) {
+ // Mergen statt Ăberschreiben: lokale, noch nicht hochgeladene Punkte
+ // (z.B. gerade erst im Admin-Panel angelegt) dĂŒrfen nicht verloren gehen.
+ const map = new Map(state.agendaCatalog.map(i => [i.id, i]));
+ items.forEach(it => map.set(it.id, Object.assign({}, map.get(it.id) || {}, it)));
+ state.agendaCatalog = Array.from(map.values());
+ }
+ ensureAgendaOrderDefaults();
+ saveState(false);
+ updateAgendaTabVisibility();
+ renderAgendaQueue();
+ } catch(e) { /* offline â zuletzt bekannten Katalog behalten */ }
+}
+
+function renderAgendaQueue() {
+ const container = document.getElementById('agenda-queue');
+ if (!container) return;
+ updateAgendaTabVisibility();
+ ensureAgendaOrderDefaults();
+ container.innerHTML = '';
+
+ const order = state.agendaOrder || [];
+ if (order.length === 0) {
+ container.innerHTML = `
+ Keine Agenda-Punkte aktiv
+ Punkte unten unter "Ausgeblendet" wieder einblenden, oder im Admin-Panel neue Punkte anlegen.
+ `;
+ renderAgendaHiddenSection();
+ return;
+ }
+
+ order.forEach(id => {
+ const it = getAgendaItem(id);
+ if (!it) return;
+ const isCurrent = it.builtin
+ ? state.mode === it.builtin
+ : (state.mode === 'agenda-item' && state.currentAgendaItemId === id);
+
+ const item = document.createElement('div');
+ item.className = 'queue-item' + (isCurrent ? ' current' : '');
+ item.dataset.id = id;
+
+ const badge = isCurrent ? `Live` : '';
+ const typeTag = it.builtin
+ ? `Vorstellungsrunde`
+ : it.type === 'embed'
+ ? `Live-Einbettung`
+ : `Bild`;
+
+ const showCall = it.builtin ? `showAgendaRound('${it.builtin}')` : `showAgendaItem('${it.id}')`;
+
+ item.innerHTML = `
+ ${badge}
+ â ż
+
+ ${escHtml(it.label || it.id)}
+ ${typeTag}
+
+
+
+
+
+ `;
+ container.appendChild(item);
+ });
+
+ renderAgendaHiddenSection();
+
+ if (sortableAgendaInstance) sortableAgendaInstance.destroy();
+ sortableAgendaInstance = Sortable.create(container, {
+ animation: 150,
+ handle: '.drag-handle',
+ ghostClass: 'sortable-ghost',
+ filter: '.empty-hint',
+ onEnd: (evt) => {
+ const moved = state.agendaOrder.splice(evt.oldIndex, 1)[0];
+ state.agendaOrder.splice(evt.newIndex, 0, moved);
+ saveState();
+ renderControl();
+ },
+ });
+}
+
+function renderAgendaHiddenSection() {
+ const section = document.getElementById('agenda-hidden-section');
+ if (!section) return;
+ if (activeTab !== 'agenda') { section.style.display = 'none'; return; }
+
+ const order = new Set(state.agendaOrder || []);
+ const allItems = [...BUILTIN_AGENDA_ITEMS, ...(state.agendaCatalog || [])];
+ const hidden = allItems.filter(it => !order.has(it.id));
+ if (hidden.length === 0) { section.style.display = 'none'; return; }
+
+ section.style.display = 'block';
+ section.className = 'absent-section';
+ let html = `Ausgeblendet`;
+ hidden.forEach(it => {
+ html += `
+ ${escHtml(it.label || it.id)}
+
+
+
+ `;
+ });
+ section.innerHTML = html;
+}
+
+function hideAgendaItem(id) {
+ state.agendaOrder = (state.agendaOrder || []).filter(aid => aid !== id);
+ if (state.mode === 'agenda-item' && state.currentAgendaItemId === id) closeAgendaItem();
+ saveState();
+ renderControl();
+}
+
+function unhideAgendaItem(id) {
+ if (!state.agendaOrder) state.agendaOrder = [];
+ if (!state.agendaOrder.includes(id)) state.agendaOrder.push(id);
+ saveState();
+ renderControl();
+}
+
+function showAgendaItem(id) {
+ const it = (state.agendaCatalog || []).find(a => a.id === id);
+ if (!it) return;
+ if (state.mode !== 'agenda-item') state._agendaPrevMode = state.mode;
+ state.mode = 'agenda-item';
+ state.currentAgendaItemId = id;
+ saveState();
+ renderControl();
+ renderDisplay();
+}
+
+function closeAgendaItem() {
+ state.mode = state._agendaPrevMode || 'members';
+ delete state._agendaPrevMode;
+ state.currentAgendaItemId = null;
+ saveState();
+ renderControl();
+ renderDisplay();
+}
+
+// Startet/holt eine der eingebauten Vorstellungsrunden auf das Display â wie ein Klick
+// auf den jeweiligen Tab, nur direkt aus der Agenda-Reihenfolge heraus nutzbar.
+function showAgendaRound(kind) {
+ if (kind === 'members') {
+ state.mode = 'members';
+ state.marketingActive = false;
+ if (state.currentIndex < 0) state.currentIndex = 0;
+ switchTab('members');
+ } else if (kind === 'marketing') {
+ // Zweiter Durchlauf durch dieselbe Mitgliederliste (Marketing-/Ergebnisrunde) â immer
+ // von vorne, ohne Redezeit-Timer.
+ state.mode = 'members';
+ state.marketingActive = true;
+ state.currentIndex = 0;
+ switchTab('members');
+ stopTimer();
+ } else {
+ state.mode = 'visitors';
+ if (state.visitorsCurrentIndex < 0) state.visitorsCurrentIndex = 0;
+ switchTab('visitors');
+ }
+ saveState();
+ renderControl();
+ renderDisplay();
+}
+
+// Eine eingebaute Vorstellungsrunde ist beim automatischen Weiterspringen nur "nutzbar", wenn
+// es dafĂŒr ĂŒberhaupt Personen gibt (z.B. keine Besucher angemeldet â Runde beim Weiter-Klick ĂŒberspringen).
+// Manuelles Anklicken in der Agenda-Liste ist davon nicht betroffen.
+function isAgendaStepUsable(id) {
+ const it = getAgendaItem(id);
+ if (!it) return false;
+ if (it.builtin === 'members' || it.builtin === 'marketing') return state.meetingOrder.length > 0;
+ if (it.builtin === 'visitors') return state.visitorsOrder.length > 0;
+ return true;
+}
+
+// Findet die ID des nĂ€chsten gĂŒltigen Punkts nach `currentId` in der aktiven Agenda-Reihenfolge
+// (Katalog-Folien + eingebaute Vorstellungsrunden). null = Ende der Agenda erreicht.
+function nextAgendaSequenceId(currentId) {
+ const order = state.agendaOrder || [];
+ const idx = order.indexOf(currentId);
+ if (idx === -1) return null;
+ for (let i = idx + 1; i < order.length; i++) {
+ if (isAgendaStepUsable(order[i])) return order[i];
+ }
+ return null;
+}
+
+// Beschriftung fĂŒr den "Weiter"-Button: zeigt, wohin der nĂ€chste Klick tatsĂ€chlich fĂŒhrt.
+function nextStepButtonLabel(afterId) {
+ const nextId = nextAgendaSequenceId(afterId);
+ if (!nextId) return 'â© Beenden';
+ const it = getAgendaItem(nextId);
+ if (!it) return 'â© Beenden';
+ if (it.builtin === 'visitors') return '2. Runde'; // vertrauter Text fĂŒr den Ăbergang mit Logo-Zwischenschritt
+ return it.label;
+}
+
+// Springt zum ĂŒbergebenen Agenda-Schritt (Folie oder eingebaute Runde), oder beendet das
+// Meeting (zurĂŒck zum Logo), wenn kein weiterer Schritt mehr folgt.
+function goToAgendaSequenceStep(id) {
+ if (!id) { endMeetingFlow(); return; }
+ const it = getAgendaItem(id);
+ if (!it) { endMeetingFlow(); return; }
+ if (it.builtin === 'visitors') {
+ // FĂŒr den Ăbergang in die Besucherrunde den bestehenden Logo-Zwischenschritt beibehalten.
+ startRoundTransition();
+ return;
+ }
+ if (it.builtin === 'members' || it.builtin === 'marketing') {
+ showAgendaRound(it.builtin);
+ return;
+ }
+ showAgendaItem(id);
+}
+
+function endMeetingFlow() {
+ state.currentIndex = -1;
+ state.visitorsCurrentIndex = -1;
+ state.mode = 'members';
+ state.marketingActive = false;
+ switchTab('members');
+ saveState();
+ renderControl();
+ renderDisplay();
+}
+
function renderVisitorsList() {
const container = document.getElementById('visitors-list');
container.innerHTML = '';
@@ -3020,6 +3518,12 @@ function updateNextButton() {
return;
}
+ if (state.mode === 'agenda-item') {
+ btn.disabled = false;
+ nameEl.textContent = nextStepButtonLabel(state.currentAgendaItemId);
+ return;
+ }
+
const next = getNextPresenter();
const hasStarted = state.currentIndex >= 0 || state.mode === 'visitors';
@@ -3041,7 +3545,7 @@ function updateNextButton() {
// Absent-summary mode
if (state.mode === 'absent-summary') {
btn.disabled = false;
- nameEl.textContent = state.visitorsOrder.length > 0 ? '2. Runde' : 'â© Beenden';
+ nameEl.textContent = nextStepButtonLabel('__members_round__');
return;
}
@@ -3055,15 +3559,18 @@ function updateNextButton() {
const visitorsDone = state.mode === 'visitors' && state.visitorsCurrentIndex >= state.visitorsOrder.length - 1;
const hasAbsent = state.absentIds.length > 0 || (state.medicalIds || []).length > 0;
- if (membersDone && hasAbsent) {
+ if (membersDone && hasAbsent && !state.marketingActive) {
btn.disabled = false;
nameEl.textContent = 'Abwesenheiten';
- } else if (membersDone && state.visitorsOrder.length === 0) {
+ } else if (membersDone && state.marketingActive) {
btn.disabled = false;
- nameEl.textContent = 'â© Beenden';
+ nameEl.textContent = nextStepButtonLabel('__marketing_round__');
+ } else if (membersDone) {
+ btn.disabled = false;
+ nameEl.textContent = nextStepButtonLabel('__members_round__');
} else if (visitorsDone) {
btn.disabled = false;
- nameEl.textContent = 'â© Beenden';
+ nameEl.textContent = nextStepButtonLabel('__visitors_round__');
} else {
btn.disabled = false;
nameEl.textContent = '';
@@ -3095,6 +3602,13 @@ function goNext() {
_coordReportClose();
return;
}
+ if (state.mode === 'agenda-item') {
+ // Folgt der Agenda-Reihenfolge zum nÀchsten Punkt. Bei eingebetteten Live-Folien kann
+ // "Weiter" nicht innerhalb der Einbettung selbst blÀttern, sondern nur zum nÀchsten
+ // Agenda-Punkt springen.
+ goToAgendaSequenceStep(nextAgendaSequenceId(state.currentAgendaItemId));
+ return;
+ }
if (state.mode === 'round-transition') {
state.mode = 'visitors';
state.visitorsCurrentIndex = 0;
@@ -3104,52 +3618,43 @@ function goNext() {
renderDisplay();
return;
}
- if (state.mode === 'members') {
+ if (state.mode === 'members' && state.marketingActive) {
+ // Marketing-/Ergebnisrunde: dieselbe Reihenfolge wie die Vorstellungsrunde, aber ohne
+ // Timer und ohne Abwesenheiten-Zwischenschritt.
if (state.currentIndex < state.meetingOrder.length - 1) {
state.currentIndex++;
} else {
- // End of members: show absent summary if applicable, otherwise go to visitors
+ goToAgendaSequenceStep(nextAgendaSequenceId('__marketing_round__'));
+ return;
+ }
+ } else if (state.mode === 'members') {
+ if (state.currentIndex < state.meetingOrder.length - 1) {
+ state.currentIndex++;
+ } else {
+ // Ende der Mitgliederrunde: erst Abwesenheiten (falls vorhanden), danach dem
+ // nÀchsten Punkt in der Agenda-Reihenfolge nach "Mitglieder-Vorstellungsrunde" folgen.
const hasAbsent = state.absentIds.length > 0 || (state.medicalIds || []).length > 0;
if (hasAbsent) {
state.mode = 'absent-summary';
- } else if (state.visitorsOrder.length > 0) {
- startRoundTransition();
+ } else {
+ goToAgendaSequenceStep(nextAgendaSequenceId('__members_round__'));
return;
}
}
} else if (state.mode === 'absent-summary') {
- if (state.visitorsOrder.length > 0) {
- startRoundTransition();
- return;
- } else {
- // End â back to logo
- state.currentIndex = -1;
- state.visitorsCurrentIndex = -1;
- state.mode = 'members';
- switchTab('members');
- }
+ goToAgendaSequenceStep(nextAgendaSequenceId('__members_round__'));
+ return;
} else if (state.mode === 'visitors') {
if (state.visitorsCurrentIndex < state.visitorsOrder.length - 1) {
state.visitorsCurrentIndex++;
} else {
- // End â back to logo
- state.currentIndex = -1;
- state.visitorsCurrentIndex = -1;
- state.mode = 'members';
- switchTab('members');
- }
- } else if (state.mode === 'members' && state.currentIndex >= state.meetingOrder.length - 1) {
- // End of members, no absent, no visitors â back to logo
- const hasAbsent = state.absentIds.length > 0 || (state.medicalIds || []).length > 0;
- if (!hasAbsent && state.visitorsOrder.length === 0) {
- state.currentIndex = -1;
- state.visitorsCurrentIndex = -1;
- state.mode = 'members';
- switchTab('members');
+ // Ende der Besucherrunde: dem nÀchsten Punkt nach "Besucher-Vorstellungsrunde" folgen.
+ goToAgendaSequenceStep(nextAgendaSequenceId('__visitors_round__'));
+ return;
}
}
- // Start timer for new presenter (2s delay)
- if (state.mode === 'members' || state.mode === 'visitors') {
+ // Start timer for new presenter (2s delay) â in der Marketing-/Ergebnisrunde bewusst kein Timer.
+ if ((state.mode === 'members' && !state.marketingActive) || state.mode === 'visitors') {
const nowPresenter = getCurrentPresenter();
if (nowPresenter) scheduleTimer(getPresenterSeconds(nowPresenter));
else stopTimer();
@@ -3167,6 +3672,7 @@ function resetMeeting() {
state.currentIndex = -1;
state.visitorsCurrentIndex = -1;
state.mode = 'members';
+ state.marketingActive = false;
switchTab('members');
saveState();
renderControl();
@@ -3177,8 +3683,13 @@ function switchTab(tab) {
activeTab = tab;
document.getElementById('member-queue').style.display = tab === 'members' ? '' : 'none';
document.getElementById('visitor-queue').style.display = tab === 'visitors' ? '' : 'none';
+ document.getElementById('agenda-queue').style.display = tab === 'agenda' ? '' : 'none';
+ const agendaHidden = document.getElementById('agenda-hidden-section');
+ if (agendaHidden && tab !== 'agenda') agendaHidden.style.display = 'none';
+ else if (agendaHidden) renderAgendaHiddenSection();
document.getElementById('tab-members').classList.toggle('active', tab === 'members');
document.getElementById('tab-visitors').classList.toggle('active', tab === 'visitors');
+ document.getElementById('tab-agenda').classList.toggle('active', tab === 'agenda');
}
function switchRightPanel(panelId) {
@@ -3352,7 +3863,17 @@ function openVisitorEdit(id) {
document.getElementById('ve-name').value = v.name || '';
document.getElementById('ve-company').value = v.company || '';
document.getElementById('ve-specialty').value = v.specialty || '';
- document.getElementById('ve-invited-by').value = v.invitedBy || '';
+ const sel = document.getElementById('ve-invited-by');
+ if (sel) {
+ sel.innerHTML = '';
+ state.members.slice().sort((a,b) => a.name.localeCompare(b.name)).forEach(m => {
+ const opt = document.createElement('option');
+ opt.value = m.id;
+ opt.textContent = m.name;
+ if (m.id === v.invitedBy) opt.selected = true;
+ sel.appendChild(opt);
+ });
+ }
document.getElementById('ve-photo').value = v.photoUrl || '';
openModal('modal-visitor-edit');
}
@@ -3366,7 +3887,7 @@ function saveVisitorEdit() {
v.name = name;
v.company = document.getElementById('ve-company').value.trim();
v.specialty = document.getElementById('ve-specialty').value.trim();
- v.invitedBy = document.getElementById('ve-invited-by').value.trim();
+ v.invitedBy = document.getElementById('ve-invited-by').value || '';
v.photoUrl = document.getElementById('ve-photo').value.trim();
saveState();
renderControl();
@@ -4244,6 +4765,296 @@ function scheduleTimer(seconds) {
}, 2000);
}
+// ============================================================
+// 8-AUGEN-GESPRĂCHE MULTI-TIMER
+// ============================================================
+const _augen = { iv: null, lastSlot: {}, lastPhase: {}, prevRunning: false };
+const AUGEN_WECHSEL_SEC = 3;
+
+function _augenCfg() {
+ if (!state.augen) {
+ state.augen = { visibleSizes: [3,4,5,6], baseSeconds: 600, startedAt: null, running: false };
+ }
+ return state.augen;
+}
+
+function _augenSpeakSecondsFor(size) {
+ // Sprechzeit pro Redner: gleiche Gesamt-Sprechzeit pro Runde (4er = Basis)
+ const cfg = _augenCfg();
+ return (4 * cfg.baseSeconds) / size;
+}
+
+function _augenSecondsFor(size) {
+ // Slot = Sprechzeit + 3s Wechsel (auĂer letzter Slot â wird separat behandelt)
+ return _augenSpeakSecondsFor(size) + AUGEN_WECHSEL_SEC;
+}
+
+function _augenTableFinishAt(size) {
+ // (size-1) volle Slots (Sprech + Wechsel) + 1 letzter Slot (nur Sprech)
+ const speak = _augenSpeakSecondsFor(size);
+ return (size - 1) * (speak + AUGEN_WECHSEL_SEC) + speak;
+}
+
+function _augenParseTime(str) {
+ const s = String(str || '').trim();
+ if (!s) return null;
+ if (s.includes(':')) {
+ const [m, sec] = s.split(':').map(x => parseInt(x, 10) || 0);
+ return m * 60 + sec;
+ }
+ const n = parseFloat(s.replace(',', '.'));
+ if (isNaN(n) || n <= 0) return null;
+ // Nur Ganzzahl â Minuten; sonst als Sekunden interpretieren wenn > 60
+ return n < 60 ? Math.round(n * 60) : Math.round(n);
+}
+
+function _augenFmt(sec) {
+ const s = Math.max(0, Math.round(sec));
+ const m = Math.floor(s / 60);
+ const r = s % 60;
+ return `${m}:${String(r).padStart(2,'0')}`;
+}
+
+function showAugenTimer() {
+ if (state.mode === 'augen-timer') { augenClose(); return; }
+ state._prevMode = state.mode;
+ state.mode = 'augen-timer';
+ _augenCfg(); // ensure exists
+ saveState();
+ renderCurrentView();
+ _augenStartTick();
+}
+
+function augenClose() {
+ _augenStopTick();
+ if (state.augen) {
+ state.augen.running = false;
+ state.augen.startedAt = null;
+ }
+ state.mode = state._prevMode || 'members';
+ delete state._prevMode;
+ saveState();
+ renderCurrentView();
+}
+
+function augenToggleRun() {
+ const cfg = _augenCfg();
+ if (cfg.running) {
+ cfg.running = false;
+ cfg.startedAt = null;
+ } else {
+ cfg.running = true;
+ cfg.startedAt = Date.now();
+ cfg._finished = false;
+ _augen.lastSlot = {};
+ _augen.lastPhase = {};
+ }
+ saveState();
+ renderAugenTimer();
+ _augenStartTick();
+}
+
+function augenReset() {
+ const cfg = _augenCfg();
+ cfg.running = false;
+ cfg.startedAt = null;
+ cfg._finished = false;
+ _augen.lastSlot = {};
+ _augen.lastPhase = {};
+ saveState();
+ renderAugenTimer();
+}
+
+function augenToggleSize(size) {
+ const cfg = _augenCfg();
+ const list = Array.isArray(cfg.visibleSizes) ? cfg.visibleSizes.slice() : [3,4,5,6];
+ const i = list.indexOf(size);
+ if (i >= 0) list.splice(i, 1); else list.push(size);
+ list.sort((a,b) => a - b);
+ if (list.length === 0) return; // mindestens einer sichtbar
+ cfg.visibleSizes = list;
+ saveState();
+ renderAugenTimer();
+}
+
+function augenApplyBaseInput() {
+ const el = document.getElementById('augen-base-input');
+ if (!el) return;
+ const sec = _augenParseTime(el.value);
+ if (!sec || sec < 10) { alert('Bitte gĂŒltige Zeit eingeben (z. B. 10:00 oder 10)'); return; }
+ const cfg = _augenCfg();
+ cfg.baseSeconds = sec;
+ // Wenn lÀuft: neu starten damit Rechnung sofort greift
+ if (cfg.running) {
+ cfg.startedAt = Date.now();
+ _augen.lastSlot = {};
+ }
+ saveState();
+ renderAugenTimer();
+}
+
+function _augenStartTick() {
+ if (_augen.iv) clearInterval(_augen.iv);
+ _augen.iv = setInterval(_augenTick, 250);
+}
+function _augenStopTick() {
+ if (_augen.iv) { clearInterval(_augen.iv); _augen.iv = null; }
+}
+
+function _augenTick() {
+ if (state.mode !== 'augen-timer') { _augenStopTick(); return; }
+ const cfg = _augenCfg();
+ if (!cfg.running || !cfg.startedAt) return;
+
+ const elapsed = (Date.now() - cfg.startedAt) / 1000;
+ const sizes = cfg.visibleSizes || [3,4,5,6];
+
+ // Ăbergangs- und Ende-Erkennung pro Tisch.
+ // Sprechzeit pro Redner = 4*base/N. Slot = Sprech + 3s Wechsel (letzter Slot: nur Sprech).
+ let allDone = true;
+ sizes.forEach(size => {
+ const speak = _augenSpeakSecondsFor(size);
+ const T = speak + AUGEN_WECHSEL_SEC;
+ const finishAt = _augenTableFinishAt(size);
+ const done = elapsed >= finishAt;
+ if (!done) allDone = false;
+
+ const speakerIdx = Math.min(size, Math.floor(elapsed / T) + 1);
+ const inSlot = elapsed - (speakerIdx - 1) * T;
+ const phase = done ? 'done' : (inSlot < speak ? 'speak' : 'wechsel');
+ const prev = _augen.lastPhase[size];
+ _augen.lastPhase[size] = phase;
+
+ if (prev === 'speak' && phase === 'wechsel') {
+ playBellSound();
+ _augenPulse(size);
+ }
+ if (prev && prev !== 'done' && phase === 'done') {
+ // Tisch fertig
+ playBellSound();
+ _augenPulse(size);
+ }
+ });
+
+ if (allDone) {
+ cfg.running = false;
+ cfg.startedAt = null;
+ cfg._finished = true;
+ saveState();
+ }
+
+ _augenUpdateTimes();
+}
+
+function _augenPulse(size) {
+ const card = document.querySelector(`.augen-card[data-size="${size}"]`);
+ if (!card) return;
+ card.classList.remove('pulse');
+ void card.offsetWidth; // reflow fĂŒr Neustart der Animation
+ card.classList.add('pulse');
+}
+
+function _augenUpdateTimes() {
+ const cfg = _augenCfg();
+ const running = !!(cfg.running && cfg.startedAt);
+ const elapsed = running ? (Date.now() - cfg.startedAt) / 1000 : 0;
+ const sizes = cfg.visibleSizes || [3,4,5,6];
+ const longestFinish = sizes.reduce((m, s) => Math.max(m, _augenTableFinishAt(s)), 0);
+ const finishedAll = !running && cfg._finished;
+
+ sizes.forEach(size => {
+ const card = document.querySelector(`.augen-card[data-size="${size}"]`);
+ if (!card) return;
+ const speak = _augenSpeakSecondsFor(size);
+ const T = speak + AUGEN_WECHSEL_SEC;
+ const finishAt = _augenTableFinishAt(size);
+ const tableDone = running ? elapsed >= finishAt : finishedAll;
+ const speakerIdx = running ? Math.min(size, Math.floor(elapsed / T) + 1) : 1;
+ const isLast = speakerIdx >= size;
+ const inSlot = running ? (elapsed - (speakerIdx - 1) * T) : 0;
+ const isWechsel = running && !tableDone && !isLast && inSlot >= speak;
+ const remSpeak = running ? Math.max(0, speak - inSlot) : speak;
+ const remWechsel = running ? Math.max(0, T - inSlot) : 0;
+ const nextIdx = Math.min(size, speakerIdx + 1);
+ const timeEl = card.querySelector('.augen-card-time');
+ const subEl = card.querySelector('.augen-card-sub');
+ card.classList.remove('warn','danger','wechsel','finished');
+ if (tableDone) {
+ if (timeEl) timeEl.textContent = 'â';
+ if (subEl) subEl.textContent = `Alle ${size} Redner durch`;
+ card.classList.add('finished');
+ } else if (isWechsel) {
+ if (timeEl) timeEl.textContent = 'Wechsel';
+ if (subEl) subEl.textContent = `Redner ${nextIdx} / ${size} in ${Math.ceil(remWechsel)}âŠ`;
+ card.classList.add('wechsel');
+ } else {
+ if (timeEl) timeEl.textContent = _augenFmt(remSpeak);
+ if (subEl) subEl.textContent = `Redner ${speakerIdx} / ${size} · Sprechzeit ${_augenFmt(speak)}`;
+ if (running) {
+ if (remSpeak <= 10) card.classList.add('danger');
+ else if (remSpeak <= 30) card.classList.add('warn');
+ }
+ }
+ });
+
+ const info = document.getElementById('augen-round-info');
+ if (info) {
+ const totalRound = 4 * cfg.baseSeconds;
+ if (running) {
+ const remRound = Math.max(0, longestFinish - elapsed);
+ info.textContent = `Rundendauer ${_augenFmt(totalRound)} · Ende in ${_augenFmt(remRound)}`;
+ } else if (finishedAll) {
+ info.textContent = `Runde beendet · Rundendauer ${_augenFmt(totalRound)}`;
+ } else {
+ info.textContent = `Rundendauer ${_augenFmt(totalRound)} · bereit`;
+ }
+ }
+ const label = document.getElementById('augen-run-btn-label');
+ if (label) label.textContent = running ? 'âž Stop' : 'â¶ Start';
+}
+
+function renderAugenTimer() {
+ const cfg = _augenCfg();
+ const grid = document.getElementById('augen-timer-grid');
+ if (!grid) return;
+ const sizes = (cfg.visibleSizes && cfg.visibleSizes.length ? cfg.visibleSizes : [4]).slice().sort((a,b) => a-b);
+ const n = sizes.length;
+ // Grid-Layout: 1 â 1 col, 2 â 2 cols, 3 â 3 cols, 4 â 2x2
+ if (n === 1) grid.style.gridTemplateColumns = '1fr';
+ else if (n === 2) grid.style.gridTemplateColumns = 'repeat(2, 1fr)';
+ else if (n === 3) grid.style.gridTemplateColumns = 'repeat(3, 1fr)';
+ else grid.style.gridTemplateColumns = 'repeat(2, 1fr)';
+ grid.style.gridAutoRows = '1fr';
+
+ grid.innerHTML = sizes.map(size => `
+
+ ${size}er Tisch
+ --:--
+
+
+ `).join('');
+
+ // Controls-Bar nur in Control-View
+ const bar = document.getElementById('augen-controls-bar');
+ if (bar) {
+ const inControl = getView() !== 'display';
+ bar.style.display = inControl ? 'flex' : 'none';
+ if (inControl) {
+ const inp = document.getElementById('augen-base-input');
+ if (inp && document.activeElement !== inp) inp.value = _augenFmt(cfg.baseSeconds);
+ const tog = document.getElementById('augen-size-toggles');
+ if (tog) {
+ tog.innerHTML = [3,4,5,6].map(s => {
+ const active = (cfg.visibleSizes || []).includes(s);
+ return ``;
+ }).join('');
+ }
+ }
+ }
+
+ _augenUpdateTimes();
+}
+
function getPresenterSeconds(presenter) {
const def = state.defaultTimer > 0 ? state.defaultTimer : 60;
if (!presenter) return def;
@@ -4463,6 +5274,7 @@ async function activateChapter() {
setTimeout(() => {
renderCurrentView();
fetchChapterMembersFromGitea();
+ fetchChapterAgendaFromGitea();
}, 800);
} catch(e) {
status.textContent = 'Verbindungsfehler. Bitte Internetverbindung prĂŒfen.';
@@ -4723,6 +5535,13 @@ function renderAdminPanel() {
Zeitanzeige aktiv
+
+ 8-Augen-GesprÀche
+
+ Multi-Tisch-Timer fĂŒr 3er/4er/5er/6er-Tische. Zeit und sichtbare Tische im Vollbild einstellbar.
+
@@ -4955,6 +5774,7 @@ if (activeChapter) applyChapterUI(activeChapter.config);
renderCurrentView();
updateTimerDisplays();
checkLicenseAndUpdates();
+if (activeChapter) fetchChapterAgendaFromGitea();
// Keyboard shortcut hint
if (getView() === 'control') {
diff --git a/screenshots/admin-panel-mitglieder.png b/screenshots/admin-panel-mitglieder.png
new file mode 100644
index 0000000..298305e
Binary files /dev/null and b/screenshots/admin-panel-mitglieder.png differ
diff --git a/screenshots/agenda-admin-liste.png b/screenshots/agenda-admin-liste.png
new file mode 100644
index 0000000..c203852
Binary files /dev/null and b/screenshots/agenda-admin-liste.png differ
diff --git a/screenshots/agenda-display.png b/screenshots/agenda-display.png
new file mode 100644
index 0000000..7c041d4
Binary files /dev/null and b/screenshots/agenda-display.png differ
diff --git a/screenshots/agenda-punkt-bild.png b/screenshots/agenda-punkt-bild.png
new file mode 100644
index 0000000..7dce0d6
Binary files /dev/null and b/screenshots/agenda-punkt-bild.png differ
diff --git a/screenshots/agenda-punkt-embed.png b/screenshots/agenda-punkt-embed.png
new file mode 100644
index 0000000..278615e
Binary files /dev/null and b/screenshots/agenda-punkt-embed.png differ
diff --git a/screenshots/agenda-steuerung.png b/screenshots/agenda-steuerung.png
new file mode 100644
index 0000000..cc83eab
Binary files /dev/null and b/screenshots/agenda-steuerung.png differ
diff --git a/screenshots/augen-timer.png b/screenshots/augen-timer.png
new file mode 100644
index 0000000..55c1411
Binary files /dev/null and b/screenshots/augen-timer.png differ
diff --git a/screenshots/besucher-eintragen.png b/screenshots/besucher-eintragen.png
new file mode 100644
index 0000000..0ede905
Binary files /dev/null and b/screenshots/besucher-eintragen.png differ
diff --git a/screenshots/besucher-vertreterliste.png b/screenshots/besucher-vertreterliste.png
new file mode 100644
index 0000000..1309227
Binary files /dev/null and b/screenshots/besucher-vertreterliste.png differ
diff --git a/screenshots/display-live.png b/screenshots/display-live.png
new file mode 100644
index 0000000..c94d1b1
Binary files /dev/null and b/screenshots/display-live.png differ
diff --git a/screenshots/display-ton-aktivieren.png b/screenshots/display-ton-aktivieren.png
new file mode 100644
index 0000000..fca46c7
Binary files /dev/null and b/screenshots/display-ton-aktivieren.png differ
diff --git a/screenshots/import-export.png b/screenshots/import-export.png
new file mode 100644
index 0000000..5007ff4
Binary files /dev/null and b/screenshots/import-export.png differ
diff --git a/screenshots/mitglieder-sync.png b/screenshots/mitglieder-sync.png
new file mode 100644
index 0000000..9f81861
Binary files /dev/null and b/screenshots/mitglieder-sync.png differ
diff --git a/screenshots/steuerung-uebersicht.png b/screenshots/steuerung-uebersicht.png
new file mode 100644
index 0000000..c15d44d
Binary files /dev/null and b/screenshots/steuerung-uebersicht.png differ
diff --git a/screenshots/vertreter-erfassen.png b/screenshots/vertreter-erfassen.png
new file mode 100644
index 0000000..a8e1889
Binary files /dev/null and b/screenshots/vertreter-erfassen.png differ
diff --git a/screenshots/zeit-pro-mitglied.png b/screenshots/zeit-pro-mitglied.png
new file mode 100644
index 0000000..84dd769
Binary files /dev/null and b/screenshots/zeit-pro-mitglied.png differ
diff --git a/screenshots/zeitanzeige-aktiv.png b/screenshots/zeitanzeige-aktiv.png
new file mode 100644
index 0000000..44b20ae
Binary files /dev/null and b/screenshots/zeitanzeige-aktiv.png differ
diff --git a/screenshots/zeiteinstellung-global.png b/screenshots/zeiteinstellung-global.png
new file mode 100644
index 0000000..a549be8
Binary files /dev/null and b/screenshots/zeiteinstellung-global.png differ
diff --git a/version.json b/version.json
index 0b44931..bf71032 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "version": "1.5",
- "notes": "Zeitanzeige oben ergÀnzt. letzte 15 Sekunden gelber Hintergrund, letzte 5 Sekunden roter Hintergrund"
-}
\ No newline at end of file
+ "version": "2.0",
+ "notes": "Agenda-Folien fĂŒr Online-Meetings (Hybrid-Chapter): Bild- oder Live-Einbettungs-Folien frei in die Reihenfolge einsortieren, inkl. Mitglieder-/Besucher-Vorstellungsrunde und neuer Marketing-/Ergebnisrunde (ohne Timer). \"Weiter\" folgt jetzt der Agenda."
+}