Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| baf236ea40 | |||
| e9a98e7423 | |||
| b6fb0f519b | |||
| 7893110125 | |||
| 7b024ae919 | |||
| d6e6809dd7 | |||
| b0c83b7b0c | |||
|
|
7c3523959e | ||
|
|
2bcf6095ab | ||
| bb9186d759 | |||
| 29f4e91e38 | |||
| ae68d34829 | |||
| 22561af0a4 | |||
| 719bb05610 | |||
| 3ef838a58b | |||
| 475aa65bc0 | |||
| 24eee60cf1 | |||
| bd1575500e | |||
| 4e2dacc50f | |||
| 9d48b427ba | |||
| 294a1be11c | |||
| 958f8e13c7 | |||
| 8aee368200 | |||
| 8c26f26e47 | |||
| b1cd3b1c5f | |||
| 64fb49f4bc | |||
| 6fa3d62d36 | |||
| f993ac91b2 | |||
| be377caf51 | |||
| 9df667e7ee | |||
| b3f4f41bfa | |||
|
|
5d9427c7a6 | ||
|
|
08340b5aec |
66
admin.html
66
admin.html
@@ -4,6 +4,9 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BNI Admin-Panel</title>
|
||||
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="favicon.ico" sizes="any">
|
||||
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||
<style>
|
||||
:root {
|
||||
--red: #cc0000; --red-dark: #990000;
|
||||
@@ -376,11 +379,12 @@
|
||||
// SHARED CONSTANTS (copy from index.html)
|
||||
// ============================================================
|
||||
const APP_VERSION = '1.1.0';
|
||||
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';
|
||||
const GITEA_RAW = 'https://git.digiseed.cloud/api/v1/repos/cheubaum/BNI-Vorstellungsrunde/raw';
|
||||
const GITEA_API = 'https://git.digiseed.cloud/api/v1/repos/cheubaum/BNI-Vorstellungsrunde';
|
||||
const CHAPTER_CONFIG_KEY = 'bni-active-chapter';
|
||||
function giteaUrl(path) { return `${GITEA_RAW}/${path}?ref=main`; }
|
||||
function giteaAuthUrl(path, token) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}`; }
|
||||
function giteaDataUrl(path) { return `${GITEA_RAW}/${path}?ref=data`; }
|
||||
function giteaAuthUrl(path, token, ref) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}${ref ? '&ref=' + ref : ''}`; }
|
||||
|
||||
// ============================================================
|
||||
// CHAPTER CONFIG
|
||||
@@ -470,7 +474,7 @@ function switchAdminTab(tab) {
|
||||
let _svAdminPhotoB64 = null;
|
||||
|
||||
function renderSvList() {
|
||||
const list = loadStammvertreter();
|
||||
const list = loadStammvertreter().sort((a, b) => (a.name || '').localeCompare(b.name || '', 'de', { sensitivity: 'base' }));
|
||||
const container = document.getElementById('admin-sv-list');
|
||||
if (!list.length) {
|
||||
container.innerHTML = `<div style="color:var(--muted);font-size:0.88rem;padding:8px 0;">Noch keine Stammvertreter gespeichert.</div>`;
|
||||
@@ -577,7 +581,7 @@ async function openImportFromChapter() {
|
||||
const listEl = document.getElementById('import-chapter-select-list');
|
||||
listEl.innerHTML = '<div style="color:var(--muted);font-size:0.85rem;">Lade Chapter…</div>';
|
||||
try {
|
||||
const res = await fetch(`${GITEA_API}/contents/chapters?ref=main`);
|
||||
const res = await fetch(`${GITEA_API}/contents/chapters?ref=data`);
|
||||
const items = await res.json();
|
||||
const chapters = Array.isArray(items) ? items.filter(i => i.type === 'dir') : [];
|
||||
if (!chapters.length) { listEl.innerHTML = '<div style="color:var(--muted);">Keine anderen Chapter gefunden.</div>'; return; }
|
||||
@@ -585,7 +589,7 @@ async function openImportFromChapter() {
|
||||
for (const ch of chapters) {
|
||||
if (activeChapter && ch.name === activeChapter.code) continue;
|
||||
try {
|
||||
const cfgRes = await fetch(giteaUrl(`chapters/${ch.name}/config.json`), { cache: 'no-store' });
|
||||
const cfgRes = await fetch(giteaDataUrl(`chapters/${ch.name}/config.json`), { cache: 'no-store' });
|
||||
const cfg = cfgRes.ok ? await cfgRes.json() : {};
|
||||
const btn = document.createElement('div');
|
||||
btn.className = 'chapter-item';
|
||||
@@ -611,7 +615,7 @@ async function loadChapterMembersForImport(code, chapterName) {
|
||||
const listEl = document.getElementById('import-member-list');
|
||||
listEl.innerHTML = '<div style="padding:12px;color:var(--muted);">Lade Mitglieder…</div>';
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${code}/members.json`));
|
||||
const res = await fetch(giteaDataUrl(`chapters/${code}/members.json`));
|
||||
if (!res.ok) { listEl.innerHTML = '<div style="padding:12px;color:#dc2626;">Keine Mitglieder gefunden.</div>'; return; }
|
||||
const data = await res.json();
|
||||
const members = data.members || [];
|
||||
@@ -669,11 +673,11 @@ async function uploadSvToGitea() {
|
||||
const path = `chapters/${activeChapter.code}/stammvertreter.json`;
|
||||
try {
|
||||
let sha = null;
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token));
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token, 'data'));
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
const body = { message: 'Stammvertreter aktualisiert', content };
|
||||
const body = { message: 'Stammvertreter aktualisiert', content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
const res = await fetch(giteaAuthUrl(path, token), { method: sha?'PUT':'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify(body) });
|
||||
const res = await fetch(giteaAuthUrl(path, token, 'data'), { method: sha?'PUT':'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify(body) });
|
||||
if (res.ok) { statusEl.textContent = `✓ ${list.length} Stammvertreter gesichert`; statusEl.style.color = 'var(--success)'; }
|
||||
else { statusEl.textContent = 'Fehler beim Upload.'; statusEl.style.color = '#dc2626'; }
|
||||
} catch(e) { statusEl.textContent = 'Verbindungsfehler.'; statusEl.style.color = '#dc2626'; }
|
||||
@@ -683,7 +687,7 @@ async function downloadSvFromGitea() {
|
||||
const statusEl = document.getElementById('sv-sync-status');
|
||||
statusEl.textContent = 'Lade…'; statusEl.style.color = 'var(--muted)';
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/stammvertreter.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/stammvertreter.json`), { cache: 'no-store' });
|
||||
if (!res.ok) { statusEl.textContent = 'Keine Daten auf Server.'; statusEl.style.color = 'var(--muted)'; return; }
|
||||
const data = await res.json();
|
||||
if (data.substitutes) { saveStammvertreter(data.substitutes); renderSvList(); statusEl.textContent = `✓ ${data.substitutes.length} Stammvertreter geladen`; statusEl.style.color = 'var(--success)'; }
|
||||
@@ -780,7 +784,7 @@ async function fetchMembersAdmin() {
|
||||
const statusEl = document.getElementById('members-status');
|
||||
statusEl.textContent = 'Lade vom Server…'; statusEl.style.color = 'var(--muted)';
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
|
||||
if (!res.ok) { statusEl.textContent = 'Keine Mitglieder auf Server.'; return; }
|
||||
const data = await res.json();
|
||||
if (!data.members || !data.members.length) { statusEl.textContent = 'Liste ist leer.'; return; }
|
||||
@@ -817,14 +821,14 @@ async function uploadMembersAdmin() {
|
||||
const data = { lastUpdated: new Date().toISOString().slice(0,10), members };
|
||||
const content = btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))));
|
||||
const path = `chapters/${activeChapter.code}/members.json`;
|
||||
const url = giteaAuthUrl(path, token);
|
||||
const url = giteaAuthUrl(path, token, 'data');
|
||||
console.log('[Upload] URL:', url);
|
||||
statusEl.textContent = 'Prüfe SHA…';
|
||||
let sha = null;
|
||||
const shaRes = await fetch(url);
|
||||
console.log('[Upload] SHA-Status:', shaRes.status);
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
const body = { message:`Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content };
|
||||
const body = { message:`Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
statusEl.textContent = sha ? 'Aktualisiere…' : 'Erstelle…';
|
||||
const res = await fetch(url, { method:sha?'PUT':'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body) });
|
||||
@@ -919,7 +923,7 @@ function editAgendaItem(id) {
|
||||
};
|
||||
thumb.onload = () => { if (thumbHint) thumbHint.style.display = 'none'; };
|
||||
if (thumbHint) thumbHint.style.display = 'none';
|
||||
thumb.src = it.imagePath.startsWith('http') ? it.imagePath : giteaUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
|
||||
thumb.src = it.imagePath.startsWith('http') ? it.imagePath : giteaDataUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
|
||||
thumb.style.display = 'block';
|
||||
} else {
|
||||
thumb.style.display = 'none';
|
||||
@@ -963,11 +967,11 @@ function handleAgendaImageFile(input) {
|
||||
|
||||
async function uploadAgendaImage(path, dataUrl, token) {
|
||||
const content = dataUrl.split(',')[1];
|
||||
const url = giteaAuthUrl(path, token);
|
||||
const url = giteaAuthUrl(path, token, 'data');
|
||||
let sha = null;
|
||||
const shaRes = await fetch(url);
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
const body = { message: `Agenda-Bild aktualisiert ${new Date().toLocaleDateString('de')}`, content };
|
||||
const body = { message: `Agenda-Bild aktualisiert ${new Date().toLocaleDateString('de')}`, content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
const res = await fetch(url, { method: sha ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
||||
if (!res.ok) { const t = await res.text(); throw new Error(`${res.status}: ${t.slice(0, 80)}`); }
|
||||
@@ -1047,7 +1051,7 @@ async function fetchAgendaAdmin() {
|
||||
const statusEl = document.getElementById('agenda-status');
|
||||
statusEl.textContent = 'Lade vom Server…'; statusEl.style.color = 'var(--muted)';
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
|
||||
if (!res.ok) { statusEl.textContent = 'Noch keine Agenda auf dem Server.'; return; }
|
||||
const data = await res.json();
|
||||
const state = loadState();
|
||||
@@ -1068,11 +1072,11 @@ async function uploadAgendaAdmin() {
|
||||
const data = { items: state.agendaCatalog || [] };
|
||||
const content = btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))));
|
||||
const path = `chapters/${activeChapter.code}/agenda.json`;
|
||||
const url = giteaAuthUrl(path, token);
|
||||
const url = giteaAuthUrl(path, token, 'data');
|
||||
let sha = null;
|
||||
const shaRes = await fetch(url);
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
const body = { message: `Agenda aktualisiert ${new Date().toLocaleDateString('de')}`, content };
|
||||
const body = { message: `Agenda aktualisiert ${new Date().toLocaleDateString('de')}`, content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
const res = await fetch(url, { method: sha ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
||||
if (res.ok) { statusEl.textContent = `✓ ${data.items.length} Agenda-Punkte hochgeladen`; statusEl.style.color = 'var(--success)'; }
|
||||
@@ -1089,16 +1093,16 @@ async function loadAllChapters() {
|
||||
const container = document.getElementById('all-chapters-list');
|
||||
container.innerHTML = '<div style="color:var(--muted);font-size:0.85rem;">Lade…</div>';
|
||||
try {
|
||||
const res = await fetch(`${GITEA_API}/contents/chapters?ref=main`);
|
||||
const res = await fetch(`${GITEA_API}/contents/chapters?ref=data`);
|
||||
const items = await res.json();
|
||||
const chapters = Array.isArray(items) ? items.filter(i => i.type === 'dir') : [];
|
||||
_allChaptersData = [];
|
||||
container.innerHTML = '';
|
||||
for (const ch of chapters) {
|
||||
try {
|
||||
const cfgRes = await fetch(giteaUrl(`chapters/${ch.name}/config.json`), { cache: 'no-store' });
|
||||
const cfgRes = await fetch(giteaDataUrl(`chapters/${ch.name}/config.json`), { cache: 'no-store' });
|
||||
const cfg = cfgRes.ok ? await cfgRes.json() : {};
|
||||
const membersRes = await fetch(giteaUrl(`chapters/${ch.name}/members.json`));
|
||||
const membersRes = await fetch(giteaDataUrl(`chapters/${ch.name}/members.json`));
|
||||
const membersData = membersRes.ok ? await membersRes.json() : {};
|
||||
_allChaptersData.push({ code: ch.name, cfg });
|
||||
const isEnabled = !!cfg.memberCoordinatorReport;
|
||||
@@ -1227,18 +1231,18 @@ async function toggleCoordinatorReport(code, currentEnabled) {
|
||||
const token = getAdminToken();
|
||||
if (!token) { alert('Bitte Admin-Token in den Einstellungen eintragen.'); return; }
|
||||
try {
|
||||
const cfgRes = await fetch(giteaUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
|
||||
const cfgRes = await fetch(giteaDataUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
|
||||
if (!cfgRes.ok) { alert('Config nicht gefunden.'); return; }
|
||||
const cfg = await cfgRes.json();
|
||||
const shaRes = await fetch(giteaAuthUrl(`chapters/${code}/config.json`, token));
|
||||
const shaRes = await fetch(giteaAuthUrl(`chapters/${code}/config.json`, token, 'data'));
|
||||
if (!shaRes.ok) { alert('SHA-Fetch fehlgeschlagen.'); return; }
|
||||
const sha = (await shaRes.json()).sha;
|
||||
cfg.memberCoordinatorReport = !currentEnabled;
|
||||
const content = btoa(unescape(encodeURIComponent(JSON.stringify(cfg, null, 2))));
|
||||
const res = await fetch(giteaAuthUrl(`chapters/${code}/config.json`, token), {
|
||||
const res = await fetch(giteaAuthUrl(`chapters/${code}/config.json`, token, 'data'), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message: `Koordinatoren-Bericht ${cfg.memberCoordinatorReport ? 'aktiviert' : 'deaktiviert'} für ${code}`, content, sha })
|
||||
body: JSON.stringify({ message: `Koordinatoren-Bericht ${cfg.memberCoordinatorReport ? 'aktiviert' : 'deaktiviert'} für ${code}`, content, sha, branch: 'data' })
|
||||
});
|
||||
if (res.ok) { loadAllChapters(); }
|
||||
else { alert('Fehler beim Speichern der Konfiguration.'); }
|
||||
@@ -1253,7 +1257,7 @@ async function loadCoordinatorReportData() {
|
||||
form.style.display = 'block';
|
||||
_coordReportData = null;
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${code}/coordinator-report-${year}.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${code}/coordinator-report-${year}.json`), { cache: 'no-store' });
|
||||
if (res.ok) _coordReportData = await res.json();
|
||||
} catch(e) {}
|
||||
if (!_coordReportData) {
|
||||
@@ -1429,14 +1433,14 @@ async function _saveCoordinatorReportToGitea(code, data) {
|
||||
_coordStatus('Speichere…');
|
||||
let sha = null;
|
||||
try {
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token));
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token, 'data'));
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
} catch(e) {}
|
||||
const content = btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))));
|
||||
const body = { message: `Update Koordinatoren-Bericht ${year} für ${code}`, content };
|
||||
const body = { message: `Update Koordinatoren-Bericht ${year} für ${code}`, content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
try {
|
||||
const res = await fetch(giteaAuthUrl(path, token), {
|
||||
const res = await fetch(giteaAuthUrl(path, token, 'data'), {
|
||||
method: sha ? 'PUT' : 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
|
||||
BIN
apple-touch-icon.png
Normal file
BIN
apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -4,6 +4,7 @@
|
||||
"id": "willkommen",
|
||||
"label": "Willkommen",
|
||||
"type": "embed",
|
||||
"notes": "wie könnte euer Produkt als Weihnachtsgeschenk vermarktet werden?\n\nVertreter:\nAlexander Kaya für Sabine Welp\nSandra Stengel für Nathalie Musto\nStefan Roth für Jennifer Born\nNiko Gäßler für Thomas Stetter\nAlexander Weimer für Jens Berger\n\n\nWert: Lebenslanges Lernen - was habt ihr dieses Jahr gelernt?",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g063c60ec44c3347ad841c139671232dff"
|
||||
},
|
||||
{
|
||||
@@ -16,6 +17,7 @@
|
||||
"id": "visitenkarten",
|
||||
"label": "Visitenkarten",
|
||||
"type": "image",
|
||||
"notes": "07:15",
|
||||
"imagePath": "agenda/visitenkarten.jpg"
|
||||
},
|
||||
{
|
||||
@@ -28,12 +30,14 @@
|
||||
"id": "bericht-des-mitgliederkoordinators",
|
||||
"label": "Bericht des Mitgliederkoordinators",
|
||||
"type": "embed",
|
||||
"notes": "07:48",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0d27561ab34c141a39f981ab6b36fbd03"
|
||||
},
|
||||
{
|
||||
"id": "break-out",
|
||||
"label": "Break Out",
|
||||
"type": "embed",
|
||||
"notes": "07:51",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0fbb79876ba15437abf469c098a72b789"
|
||||
},
|
||||
{
|
||||
@@ -46,6 +50,7 @@
|
||||
"id": "marketing-ergebnisrunde-erklaerung",
|
||||
"label": "Marketing- Ergebnisrunde Erklärung",
|
||||
"type": "embed",
|
||||
"notes": "08:12",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0848053a6eea54f18bbaaee7d33861386"
|
||||
},
|
||||
{
|
||||
@@ -58,18 +63,21 @@
|
||||
"id": "ziehung-des-tagespreises",
|
||||
"label": "Ziehung des Tagespreises",
|
||||
"type": "embed",
|
||||
"notes": "08:20",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0b231f4b1bdf34850a8a8baca4b2d6972"
|
||||
},
|
||||
{
|
||||
"id": "bericht-des-schatzmeisters",
|
||||
"label": "Bericht des Schatzmeisters",
|
||||
"type": "embed",
|
||||
"notes": "wer am Termin in Dellmensingen nicht da ist, denk daran das Geld davor an Jens zu geben / überweisen - wir bringen ihn sonst in die blöde Lage, dass er privat das Geld auslegen muss",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0249a5f37a97e419db184bef50bcbdb8e"
|
||||
},
|
||||
{
|
||||
"id": "bericht-des-eventkoordinators",
|
||||
"label": "Bericht des Eventkoordinators",
|
||||
"type": "embed",
|
||||
"notes": "08:23",
|
||||
"embedUrl": "https://show.zohopublic.eu/publish/cb5g0ad05a18b97214634818823cd3bd3ebca"
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
10
favicon.svg
Normal file
10
favicon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<g stroke="#F5D822" stroke-width="3.5" stroke-linecap="round">
|
||||
<path d="M32 2v8M32 54v8M2 32h8M54 32h8M10.8 10.8l5.7 5.7M47.5 47.5l5.7 5.7M10.8 53.2l5.7-5.7M47.5 16.5l5.7-5.7"/>
|
||||
</g>
|
||||
<circle cx="32" cy="32" r="18" fill="#F5D822" stroke="#C70607" stroke-width="3"/>
|
||||
<g fill="none" stroke="#C70607" stroke-width="2.6" stroke-linecap="round">
|
||||
<path d="M23 29.5q3-4 6 0M35 29.5q3-4 6 0"/>
|
||||
<path d="M23.5 36q8.5 8 17 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 512 B |
62
index.html
62
index.html
@@ -4,6 +4,9 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BNI Chapter Sonne – Vorstellungsrunde</title>
|
||||
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="favicon.ico" sizes="any">
|
||||
<link rel="apple-touch-icon" href="apple-touch-icon.png">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.js"></script>
|
||||
@@ -1734,7 +1737,7 @@
|
||||
|
||||
<div id="sub-sv-picker" style="display:none;margin-bottom:12px;">
|
||||
<div style="font-size:0.75rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;">Stammvertreter wählen</div>
|
||||
<div id="sub-sv-list" style="display:flex;flex-wrap:wrap;gap:8px;max-height:110px;overflow-y:auto;"></div>
|
||||
<select id="sub-sv-select" onchange="if(this.value) pickStammvertreter(this.value)" style="width:100%;padding:8px;border:1px solid var(--border);border-radius:6px;font-size:0.9rem;"></select>
|
||||
<hr style="margin:12px 0 0;border:none;border-top:1px solid var(--border);">
|
||||
</div>
|
||||
|
||||
@@ -1943,11 +1946,12 @@
|
||||
// ============================================================
|
||||
// CONSTANTS & CHAPTER CONFIG
|
||||
// ============================================================
|
||||
const APP_VERSION = '2.1';
|
||||
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';
|
||||
const APP_VERSION = '2.2';
|
||||
const GITEA_RAW = 'https://git.digiseed.cloud/api/v1/repos/cheubaum/BNI-Vorstellungsrunde/raw';
|
||||
const GITEA_API = 'https://git.digiseed.cloud/api/v1/repos/cheubaum/BNI-Vorstellungsrunde';
|
||||
function giteaUrl(path) { return `${GITEA_RAW}/${path}?ref=main`; }
|
||||
function giteaAuthUrl(path, token) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}`; }
|
||||
function giteaDataUrl(path) { return `${GITEA_RAW}/${path}?ref=data`; }
|
||||
function giteaAuthUrl(path, token, ref) { return `${GITEA_API}/contents/${path}?token=${encodeURIComponent(token)}${ref ? '&ref=' + ref : ''}`; }
|
||||
const CHAPTER_CONFIG_KEY = 'bni-active-chapter';
|
||||
|
||||
function loadChapterConfig() {
|
||||
@@ -2132,10 +2136,14 @@ function deleteStammvertreter(svId) {
|
||||
renderStammvertreterSection();
|
||||
}
|
||||
|
||||
function sortSvByName(list) {
|
||||
return [...list].sort((a, b) => (a.name || '').localeCompare(b.name || '', 'de', { sensitivity: 'base' }));
|
||||
}
|
||||
|
||||
function renderStammvertreterSection() {
|
||||
const container = document.getElementById('sv-list-container');
|
||||
if (!container) return;
|
||||
const list = loadStammvertreter();
|
||||
const list = sortSvByName(loadStammvertreter());
|
||||
if (list.length === 0) {
|
||||
container.innerHTML = `<div style="color:var(--muted);font-size:0.82rem;padding:2px 0 8px;">Noch keine gespeichert. → <a href="admin.html" target="_blank" style="color:var(--red);">Admin öffnen</a> zum Importieren aus anderen Chaptern.</div>`;
|
||||
return;
|
||||
@@ -2153,23 +2161,15 @@ function renderStammvertreterSection() {
|
||||
}
|
||||
|
||||
function renderSvPicker() {
|
||||
const list = loadStammvertreter();
|
||||
const list = sortSvByName(loadStammvertreter());
|
||||
const picker = document.getElementById('sub-sv-picker');
|
||||
const container = document.getElementById('sub-sv-list');
|
||||
if (!picker || !container) return;
|
||||
const select = document.getElementById('sub-sv-select');
|
||||
if (!picker || !select) return;
|
||||
if (list.length === 0) { picker.style.display = 'none'; return; }
|
||||
picker.style.display = '';
|
||||
container.innerHTML = '';
|
||||
list.forEach(sv => {
|
||||
const photoHtml = sv.photoB64
|
||||
? `<img src="${sv.photoB64}" style="width:32px;height:32px;border-radius:50%;object-fit:cover;">`
|
||||
: `<div style="width:32px;height:32px;border-radius:50%;background:${getColor(sv.name)};display:flex;align-items:center;justify-content:center;color:white;font-size:0.68rem;font-weight:700;">${getInitials(sv.name)}</div>`;
|
||||
const card = document.createElement('div');
|
||||
card.style.cssText = 'display:flex;align-items:center;gap:7px;padding:6px 9px;background:#f8f9fa;border:1px solid var(--border);border-radius:8px;cursor:pointer;';
|
||||
card.innerHTML = `${photoHtml}<div><div style="font-size:0.79rem;font-weight:600;">${escHtml(sv.name)}</div><div style="font-size:0.69rem;color:var(--muted);">${escHtml(sv.company||'')}</div></div>`;
|
||||
card.onclick = () => pickStammvertreter(sv.id);
|
||||
container.appendChild(card);
|
||||
});
|
||||
select.innerHTML = '<option value="">– Stammvertreter auswählen –</option>' + list.map(sv =>
|
||||
`<option value="${escHtml(sv.id)}">${escHtml(sv.name)}${sv.company ? ' · ' + escHtml(sv.company) : ''}</option>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
function pickStammvertreter(svId) {
|
||||
@@ -2509,7 +2509,7 @@ function renderDisplay() {
|
||||
imgEl.src = '';
|
||||
} else if (it.imagePath) {
|
||||
const src = it.imagePath.startsWith('http') ? it.imagePath
|
||||
: giteaUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
|
||||
: giteaDataUrl(`chapters/${activeChapter.code}/${it.imagePath}`);
|
||||
imgEl.onerror = () => {
|
||||
imgEl.style.display = 'none';
|
||||
if (errEl) errEl.style.display = 'block';
|
||||
@@ -3234,7 +3234,7 @@ async function fetchChapterAgendaFromGitea() {
|
||||
if (!state.agendaCatalog) state.agendaCatalog = [];
|
||||
ensureAgendaOrderDefaults();
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/agenda.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`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
|
||||
@@ -5337,7 +5337,7 @@ async function activateChapter() {
|
||||
status.textContent = 'Überprüfe Lizenzcode…';
|
||||
status.style.color = 'rgba(255,255,255,0.6)';
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${code}/config.json`), { cache: 'no-store' });
|
||||
if (!res.ok) {
|
||||
status.textContent = 'Ungültiger oder abgelaufener Lizenzcode.';
|
||||
status.style.color = '#f87171';
|
||||
@@ -5379,7 +5379,7 @@ function applyChapterUI(config) {
|
||||
if (ctrlName) ctrlName.textContent = `${name} – Steuerung`;
|
||||
if (config.logoUrl && activeChapter) {
|
||||
const src = config.logoUrl.startsWith('http') ? config.logoUrl
|
||||
: giteaUrl(`chapters/${activeChapter.code}/${config.logoUrl}`);
|
||||
: giteaDataUrl(`chapters/${activeChapter.code}/${config.logoUrl}`);
|
||||
['display-logo','display-logo-empty','ctrl-logo'].forEach(id => {
|
||||
const img = document.getElementById(id);
|
||||
if (img) img.src = src;
|
||||
@@ -5397,7 +5397,7 @@ function applyChapterUI(config) {
|
||||
async function checkLicenseAndUpdates() {
|
||||
if (!activeChapter) return;
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
|
||||
if (!res.ok) {
|
||||
clearChapterData();
|
||||
state = defaultState();
|
||||
@@ -5441,7 +5441,7 @@ function showUpdateBanner(version, notes) {
|
||||
|
||||
async function downloadUpdate(version) {
|
||||
try {
|
||||
const licRes = await fetch(giteaUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
|
||||
const licRes = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/config.json`), { cache: 'no-store' });
|
||||
if (!licRes.ok) {
|
||||
clearChapterData();
|
||||
state = defaultState();
|
||||
@@ -5502,7 +5502,7 @@ async function downloadUpdate(version) {
|
||||
async function fetchChapterMembersFromGitea() {
|
||||
if (!activeChapter) return;
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/members.json`), { cache: 'no-store' });
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
if (!data.members || !data.members.length) return;
|
||||
@@ -5563,13 +5563,13 @@ async function uploadMembersToGitea() {
|
||||
try {
|
||||
// Get current SHA
|
||||
let sha = null;
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token));
|
||||
const shaRes = await fetch(giteaAuthUrl(path, token, 'data'));
|
||||
if (shaRes.ok) sha = (await shaRes.json()).sha;
|
||||
|
||||
const body = { message: `Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content };
|
||||
const body = { message: `Mitglieder aktualisiert ${new Date().toLocaleDateString('de')}`, content, branch: 'data' };
|
||||
if (sha) body.sha = sha;
|
||||
|
||||
const res = await fetch(giteaAuthUrl(path, token), {
|
||||
const res = await fetch(giteaAuthUrl(path, token, 'data'), {
|
||||
method: sha ? 'PUT' : 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
@@ -5821,7 +5821,7 @@ async function showCoordinatorReport() {
|
||||
];
|
||||
const datas = await Promise.all(candidates.map(async f => {
|
||||
try {
|
||||
const res = await fetch(giteaUrl(`chapters/${activeChapter.code}/${f}`), { cache: 'no-store' });
|
||||
const res = await fetch(giteaDataUrl(`chapters/${activeChapter.code}/${f}`), { cache: 'no-store' });
|
||||
if (!res.ok) return null;
|
||||
return await res.json();
|
||||
} catch(e) { return null; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "2.1",
|
||||
"notes": "Agenda-Folien: Präsentations-Klicker funktioniert jetzt auch dann weiter, wenn der Fokus auf dem Display-(Beamer)-Fenster liegt statt im Steuerpult (Pfeiltaste rechts/Leertaste), zusätzlich ein dezenter Klick-Button unten rechts im Display. Außerdem: Sprechernotizen pro Agenda-Punkt, die nur im Steuerpult sichtbar sind, nie auf dem Display/Beamer."
|
||||
}
|
||||
"version": "2.2",
|
||||
"notes": "Neue, verschlüsselte Serveradresse. Bitte jetzt aktualisieren – die alte Version kann ab 1. November keine Daten mehr laden oder speichern."
|
||||
}
|
||||
Reference in New Issue
Block a user