From 05ed85bc2e59ab899125d57957459bc37d2b32f5 Mon Sep 17 00:00:00 2001 From: Jeremy Kirsch Date: Mon, 18 May 2026 15:39:43 +0200 Subject: [PATCH] create credits page --- public/index.html | 283 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 6c68a6f..3819d7b 100644 --- a/public/index.html +++ b/public/index.html @@ -254,6 +254,33 @@ body::before{content:'';position:fixed;inset:0;background-image:radial-gradient( .toast-danger{background:rgba(192,64,64,.18);border-color:var(--red2);color:var(--red2);} .toast-info{background:rgba(74,122,192,.18);border-color:var(--blue2);color:var(--blue2);} + +/* ═══════════ CREDITS PAGE ═══════════ */ +.credits-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;margin-bottom:32px;} +.credit-card{background:var(--bg2);border:1px solid var(--border2);border-radius:6px;padding:22px 24px;transition:all .22s;} +.credit-card:hover{border-color:var(--border3);transform:translateY(-2px);} +.credit-card.partner{border-color:rgba(200,169,110,.3);background:linear-gradient(135deg,var(--bg2) 0%,rgba(200,169,110,.04) 100%);} +.credit-avatar{width:56px;height:56px;border-radius:50%;object-fit:cover;background:var(--bg4);border:2px solid var(--border2);flex-shrink:0;} +.credit-avatar-placeholder{width:56px;height:56px;border-radius:50%;background:var(--bg4);border:2px solid var(--border2);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;} +.credit-header{display:flex;align-items:center;gap:14px;margin-bottom:12px;} +.credit-name{font-family:'Cinzel',serif;font-size:16px;font-weight:600;color:var(--accent2);letter-spacing:.5px;} +.credit-role{font-family:'Share Tech Mono',monospace;font-size:9px;letter-spacing:2px;text-transform:uppercase;color:var(--muted);margin-top:3px;} +.credit-role.partner-role{color:var(--accent);} +.credit-desc{font-size:13.5px;color:var(--muted2);line-height:1.65;} +.credit-links{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;} +.credit-link{font-family:'Share Tech Mono',monospace;font-size:9px;letter-spacing:1.5px;text-transform:uppercase;padding:4px 10px;border-radius:2px;background:var(--bg4);border:1px solid var(--border2);color:var(--muted2);text-decoration:none;transition:all .18s;} +.credit-link:hover{color:var(--text);border-color:var(--border3);} +.credit-link.discord{background:rgba(88,101,242,.12);border-color:rgba(88,101,242,.35);color:#7289da;} +.credit-link.discord:hover{background:rgba(88,101,242,.22);} +.credit-link.roblox{background:rgba(192,64,64,.1);border-color:rgba(192,64,64,.3);color:var(--red2);} +.credit-link.roblox:hover{background:rgba(192,64,64,.2);} +.credit-link.website{background:rgba(74,122,192,.1);border-color:rgba(74,122,192,.3);color:var(--blue2);} +.credit-link.website:hover{background:rgba(74,122,192,.2);} +.partner-badge{font-family:'Share Tech Mono',monospace;font-size:9px;letter-spacing:2px;text-transform:uppercase;padding:2px 8px;border-radius:2px;background:rgba(200,169,110,.15);border:1px solid rgba(200,169,110,.35);color:var(--accent2);margin-left:auto;flex-shrink:0;align-self:flex-start;} +.credits-section-title{font-family:'Cinzel',serif;font-size:14px;color:var(--accent);letter-spacing:3px;text-transform:uppercase;margin-bottom:16px;} +.credits-empty{text-align:center;padding:40px 24px;color:var(--muted);font-style:italic;font-size:14px;background:var(--bg3);border:1px dashed var(--border2);border-radius:6px;} +.add-credit-form{background:var(--bg3);border:1px solid var(--border2);border-radius:6px;padding:22px 24px;margin-bottom:28px;} + @media(max-width:640px){.site-title{font-size:28px;letter-spacing:4px;}.form-row{grid-template-columns:1fr;}.ghost-grid{grid-template-columns:1fr;}.modal-hd,.modal-body{padding-left:18px;padding-right:18px;}.login-box{padding:32px 24px;}.login-logo{font-size:34px;letter-spacing:6px;}} @@ -312,6 +339,7 @@ body::before{content:'';position:fixed;inset:0;background-image:radial-gradient( +
Evidence Filter — einmal: gefunden  ·  zweimal: ausgeschlossen
@@ -347,6 +375,28 @@ body::before{content:'';position:fixed;inset:0;background-image:radial-gradient(
+ +
+
+
+
+
Credits & Partner
+
Danksagungen · Partner · Community
+
+ +
+
+
Partner
+
+
+
+
Credits
+
+
+
+
@@ -447,6 +497,77 @@ body::before{content:'';position:fixed;inset:0;background-image:radial-gradient( + + + +
Sicher?
@@ -522,12 +643,14 @@ function setupUI() { if(u.isAdmin) document.getElementById('pill-admin').style.display='block'; buildCards(); buildCheat(); populateGhostSelect(); + loadCredits(); if(u.isAdmin) updateAdminBadge(); } function setupGuestUI() { document.getElementById('guest-badge').style.display='block'; buildCards(); buildCheat(); + loadCredits(); } /* ── AUTH ── */ @@ -639,10 +762,13 @@ function setMode(m) { } function switchTab(t) { - ['cards','cheat','guide'].forEach((x,i)=>{ - document.getElementById('vtab-'+x).classList.toggle('active',x===t); - document.querySelectorAll('#vnav .nav-btn')[i].classList.toggle('active',x===t); + ['cards','cheat','guide','credits'].forEach((x,i)=>{ + const el = document.getElementById('vtab-'+x); + if(el) el.classList.toggle('active',x===t); + const btn = document.querySelectorAll('#vnav .nav-btn')[i]; + if(btn) btn.classList.toggle('active',x===t); }); + if(t==='credits') renderCredits(); } /* ── ADMIN ── */ @@ -845,6 +971,147 @@ function hideLogin(){document.getElementById('login-screen').classList.remove('s function fmtTime(ts){if(!ts)return'—';const d=new Date(ts);return d.toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'})+' '+d.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'});} function escH(s){return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');} + +/* ═══════════════════════════════════════════════════ + CREDITS +═══════════════════════════════════════════════════ */ +let credits = []; +let editingCreditId = null; + +// Load from localStorage (no separate API needed — admin manages locally) +function loadCredits() { + try { credits = JSON.parse(localStorage.getItem('blair_credits') || '[]'); } + catch { credits = []; } +} +function saveCredits() { + localStorage.setItem('blair_credits', JSON.stringify(credits)); +} + +function renderCredits() { + loadCredits(); + const partners = credits.filter(c => c.type === 'partner'); + const crds = credits.filter(c => c.type === 'credit'); + + const partnerGrid = document.getElementById('credits-partners-grid'); + const creditsGrid = document.getElementById('credits-credits-grid'); + const partnerSec = document.getElementById('credits-partners-section'); + const creditsSec = document.getElementById('credits-credits-section'); + + // Partners + if (!partners.length) { + partnerGrid.innerHTML = '
Noch keine Partner eingetragen.
'; + } else { + partnerGrid.innerHTML = ''; + partners.forEach(c => partnerGrid.appendChild(buildCreditCard(c))); + } + + // Credits + if (!crds.length) { + creditsGrid.innerHTML = '
Noch keine Credits eingetragen.
'; + } else { + creditsGrid.innerHTML = ''; + crds.forEach(c => creditsGrid.appendChild(buildCreditCard(c))); + } + + // Show add button for admins + const adminBtn = document.getElementById('credits-admin-btn'); + if (adminBtn) adminBtn.style.display = (currentUser?.isAdmin) ? 'block' : 'none'; +} + +function buildCreditCard(c) { + const card = document.createElement('div'); + card.className = 'credit-card' + (c.type === 'partner' ? ' partner' : ''); + + const avatarHtml = c.avatar + ? `${escH(c.name)}` + + `` + : `
${c.emoji || '👤'}
`; + + const linksHtml = [ + c.discord ? `Discord` : '', + c.website ? `Website` : '', + c.roblox ? `Roblox` : '', + ].filter(Boolean).join(''); + + const adminBtns = currentUser?.isAdmin + ? `
+ + +
` : ''; + + card.innerHTML = ` +
+ ${avatarHtml} +
+
${escH(c.name)}
+
${escH(c.role || '')}
+
+ ${c.type === 'partner' ? 'Partner' : ''} +
+ ${c.desc ? `
${escH(c.desc)}
` : ''} + ${linksHtml ? `` : ''} + ${adminBtns}`; + + return card; +} + +function openCreditForm(id) { + const c = id ? credits.find(x => x.id === id) : null; + editingCreditId = id || null; + document.getElementById('cm-title').textContent = c ? `Bearbeiten: ${c.name}` : 'Eintrag hinzufügen'; + document.getElementById('cf-name').value = c?.name || ''; + document.getElementById('cf-type').value = c?.type || 'credit'; + document.getElementById('cf-role').value = c?.role || ''; + document.getElementById('cf-avatar').value = c?.avatar || ''; + document.getElementById('cf-emoji').value = c?.emoji || ''; + document.getElementById('cf-desc').value = c?.desc || ''; + document.getElementById('cf-discord').value = c?.discord || ''; + document.getElementById('cf-website').value = c?.website || ''; + document.getElementById('cf-roblox').value = c?.roblox || ''; + openModal('credits-modal'); +} + +function saveCreditEntry() { + const name = document.getElementById('cf-name').value.trim(); + if (!name) { toast("Name darf nicht leer sein!", "danger"); return; } + + const entry = { + id: editingCreditId || 'c_' + Date.now().toString(36), + name, + type: document.getElementById('cf-type').value, + role: document.getElementById('cf-role').value.trim(), + avatar: document.getElementById('cf-avatar').value.trim(), + emoji: document.getElementById('cf-emoji').value.trim() || '👤', + desc: document.getElementById('cf-desc').value.trim(), + discord: document.getElementById('cf-discord').value.trim(), + website: document.getElementById('cf-website').value.trim(), + roblox: document.getElementById('cf-roblox').value.trim(), + }; + + if (editingCreditId) { + const idx = credits.findIndex(c => c.id === editingCreditId); + if (idx > -1) credits[idx] = entry; + } else { + credits.push(entry); + } + + saveCredits(); + closeModal('credits-modal'); + renderCredits(); + toast(`✓ ${name} gespeichert!`, "success"); +} + +function confirmDeleteCredit(id) { + const c = credits.find(x => x.id === id); + if (!c) return; + showConfirm(`${c.name} entfernen?`, 'Dieser Eintrag wird aus Credits & Partner entfernt.', () => { + credits = credits.filter(x => x.id !== id); + saveCredits(); + renderCredits(); + toast(`${c.name} entfernt.`, "danger"); + }); +} + /* ── BOOT ── */ document.addEventListener('DOMContentLoaded', () => { @@ -912,6 +1179,16 @@ document.addEventListener('DOMContentLoaded', () => { const guestBtn = document.getElementById('guest-btn'); if (guestBtn) guestBtn.addEventListener('click', guestMode); + // ── Credits ── + document.getElementById('close-credits-modal').addEventListener('click', () => closeModal('credits-modal')); + document.getElementById('btn-cancel-credits').addEventListener('click', () => closeModal('credits-modal')); + document.getElementById('btn-save-credit').addEventListener('click', saveCreditEntry); + document.getElementById('credits-modal').addEventListener('click', (e) => { + if (e.target === document.getElementById('credits-modal')) closeModal('credits-modal'); + }); + const addCreditBtn = document.getElementById('btn-add-credit'); + if (addCreditBtn) addCreditBtn.addEventListener('click', () => openCreditForm(null)); + init(); });