/* ============================================================ public/js/buildings/gildenhalle.js ============================================================ */ let gh_initialized = false; let gh_page = 1; let gh_search = ''; let gh_playerGuild = null; let gh_isGuildLeader = false; function ghLoadCSS() { if (!document.querySelector('link[href="/css/gildenhalle.css"]')) { const l = document.createElement('link'); l.rel = 'stylesheet'; l.href = '/css/gildenhalle.css'; document.head.appendChild(l); } } /* ── Popup sicherstellen ────────────────────────────────── */ function ghEnsurePopup() { if (document.getElementById('gildenhalle-popup')) return; const popup = document.createElement('div'); popup.id = 'gildenhalle-popup'; popup.className = 'qm-popup'; popup.innerHTML = `
⚔ Gildenhalle
Gilden
Lade Gilden…
Eigene Gilde
Lade…
Gilden Aufgaben
Lade Aufgaben…
`; document.body.appendChild(popup); popup.querySelector('#gh-close').addEventListener('click', ghClose); /* Tab-Wechsel */ popup.querySelectorAll('.mp-tab').forEach(btn => { btn.addEventListener('click', () => { popup.querySelectorAll('.mp-tab').forEach(t => t.classList.remove('mp-tab-active')); popup.querySelectorAll('.mp-panel').forEach(p => p.classList.remove('active')); btn.classList.add('mp-tab-active'); const panel = document.getElementById(btn.dataset.tab); panel?.classList.add('active'); if (btn.dataset.tab === 'gh-panel-2') ghLoadMyGuild(); if (btn.dataset.tab === 'gh-panel-3') ghLoadTasks(); }); }); /* Gründen-Toggle */ function ghUpdateCreateBtn() { const btn = document.getElementById('gh-create-toggle-btn'); if (!btn) return; if (gh_isGuildLeader) { btn.disabled = true; btn.title = 'Gildenmeister können keine weitere Gilde gründen'; btn.style.opacity = '0.4'; btn.style.cursor = 'not-allowed'; } else { btn.disabled = false; btn.title = ''; btn.style.opacity = ''; btn.style.cursor = ''; } } document.getElementById('gh-create-toggle-btn').addEventListener('click', () => { if (gh_isGuildLeader) return; document.getElementById('gh-create-form-wrap').style.display = 'block'; document.getElementById('gh-create-error').textContent = ''; }); document.getElementById('gh-create-cancel').addEventListener('click', () => { document.getElementById('gh-create-form-wrap').style.display = 'none'; }); document.getElementById('gh-create-submit').addEventListener('click', ghCreateGuild); /* Suche */ document.getElementById('gh-search-btn').addEventListener('click', () => { gh_search = document.getElementById('gh-search-input').value.trim(); gh_page = 1; ghLoadGuildList(); }); document.getElementById('gh-search-input').addEventListener('keydown', e => { if (e.key === 'Enter') document.getElementById('gh-search-btn').click(); }); /* Drag */ const header = popup.querySelector('.qm-popup-header'); let dragging=false, sx,sy,sl,st; header.style.cursor='grab'; header.addEventListener('mousedown', e => { if (e.target.classList.contains('qm-popup-close')) return; dragging=true; header.style.cursor='grabbing'; const r=popup.getBoundingClientRect(); sx=e.clientX; sy=e.clientY; sl=r.left; st=r.top; popup.style.transform='none'; popup.style.left=sl+'px'; popup.style.top=st+'px'; e.preventDefault(); }); document.addEventListener('mousemove', e => { if (!dragging) return; popup.style.left=(sl+(e.clientX-sx))+'px'; popup.style.top=(st+(e.clientY-sy))+'px'; }); document.addEventListener('mouseup', () => { dragging=false; header.style.cursor='grab'; }); } /* ════════════════════════════════════════════ PANEL 1: Gilden-Liste laden ════════════════════════════════════════════ */ async function ghLoadGuildList() { const grid = document.getElementById('gh-guild-grid'); const pagination = document.getElementById('gh-pagination'); if (!grid) return; grid.innerHTML = '
Lade Gilden…
'; if (pagination) pagination.innerHTML = ''; try { const res = await fetch(`/api/gildenhalle/list?search=${encodeURIComponent(gh_search)}&page=${gh_page}`); const data = await res.json(); gh_playerGuild = data.playerGuildId; gh_isGuildLeader = data.isGuildLeader || false; ghUpdateCreateBtn(); if (!data.guilds.length) { grid.innerHTML = '
Keine Gilden gefunden.
'; return; } grid.innerHTML = data.guilds.map(g => `
[${g.tag}] ${g.name}
${g.description || 'Keine Beschreibung'}
Lv. ${g.level} · ${g.member_count}/${g.max_members} Mitgl. ${g.open ? '✔ Offen' : '🔒 Anfrage'}
`).join(''); grid.querySelectorAll('.gh-join-btn:not([disabled])').forEach(btn => { btn.addEventListener('click', () => ghJoinGuild( parseInt(btn.dataset.id), btn.dataset.open === '1', btn )); }); ghRenderPagination(pagination, data.totalPages, data.total); } catch (err) { grid.innerHTML = '
Fehler beim Laden.
'; console.error('[GH]', err); } } /* ── Gilde beitreten ────────────────────────────────────── */ async function ghJoinGuild(guildId, isOpen, btn) { btn.disabled = true; btn.textContent = '…'; let message = null; if (!isOpen) { message = window.prompt('Nachricht an die Gilde (optional):') ?? ''; } try { const res = await fetch(`/api/gildenhalle/join/${guildId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message }), }); const data = await res.json(); if (!res.ok) { alert(data.error || 'Fehler'); btn.disabled=false; btn.textContent=isOpen?'⚔ Beitreten':'✉ Anfrage stellen'; return; } if (data.joined) { ghShowToast('✔ Erfolgreich der Gilde beigetreten!'); gh_page = 1; ghLoadGuildList(); } else { btn.classList.add('pending'); btn.textContent = '⏳ Anfrage ausstehend'; ghShowToast('✉ Beitrittsanfrage gesendet!'); } } catch { btn.disabled=false; } } /* ── Gilde gründen ──────────────────────────────────────── */ async function ghCreateGuild() { const name = document.getElementById('gh-new-name').value.trim(); const tag = document.getElementById('gh-new-tag').value.trim().toUpperCase(); const desc = document.getElementById('gh-new-desc').value.trim(); const open = document.getElementById('gh-new-open').value; const errEl = document.getElementById('gh-create-error'); errEl.textContent = ''; if (!name || !tag) { errEl.textContent = 'Name und Tag sind Pflichtfelder.'; return; } const btn = document.getElementById('gh-create-submit'); btn.disabled = true; btn.textContent = '…'; try { const res = await fetch('/api/gildenhalle/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, tag, description: desc, open: open === '1' }), }); const data = await res.json(); if (!res.ok) { errEl.textContent = data.error || 'Fehler'; btn.disabled=false; btn.textContent='⚔ GILDE GRÜNDEN'; return; } document.getElementById('gh-create-form-wrap').style.display = 'none'; ghShowToast('🏰 Gilde erfolgreich gegründet!'); gh_page = 1; ghLoadGuildList(); // Wechsel zu "Eigene Gilde" setTimeout(() => { document.querySelector('[data-tab="gh-panel-2"]')?.click(); }, 500); } catch { btn.disabled=false; btn.textContent='⚔ GILDE GRÜNDEN'; } } /* ════════════════════════════════════════════ PANEL 2: Eigene Gilde ════════════════════════════════════════════ */ async function ghLoadMyGuild() { const container = document.getElementById('gh-my-content'); if (!container) return; container.innerHTML = '
Lade…
'; try { const res = await fetch('/api/gildenhalle/my'); const data = await res.json(); if (!data.guild) { container.innerHTML = `
🏰
Du bist noch in keiner Gilde.
Tritt einer Gilde bei oder gründe eine eigene.
`; return; } const { guild, members, ranks, requests } = data; const isLeader = (guild.leader_id === /* wird per Session gesetzt */ null); // Mitglieder-Tabelle const membersHtml = members.map(m => ` ${m.name} Lv. ${m.level} ${guild.can_manage_ranks && m.id !== guild.leader_id ? `` : `${m.rank_name || '–'}` } ${new Date(m.joined_at).toLocaleDateString('de-DE')} ${guild.can_kick && m.id !== guild.leader_id ? `` : ''} `).join(''); // Anfragen const requestsHtml = requests.length > 0 ? `
Beitrittsanfragen (${requests.length})
${requests.map(r => `
${r.name} ${r.message || '–'} ${guild.can_invite ? ` ` : ''}
`).join('')}
` : ''; container.innerHTML = `
[${guild.tag}]
${guild.name}
Lv. ${guild.level} · ${members.length}/${guild.max_members} Mitglieder
${requestsHtml}
Mitglieder
${membersHtml}
NameLevelRangBeigetreten
`; } catch (err) { container.innerHTML = '
Fehler beim Laden.
'; console.error('[GH/my]', err); } } /* ── Rang ändern ────────────────────────────────────────── */ window.ghChangeRank = async function(userId, rankId) { try { const res = await fetch(`/api/gildenhalle/member/${userId}/rank`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rank_id: rankId }), }); if (!res.ok) { const d=await res.json(); alert(d.error||'Fehler'); ghLoadMyGuild(); } else ghShowToast('✔ Rang geändert'); } catch {} }; /* ── Mitglied kicken ────────────────────────────────────── */ window.ghKickMember = async function(userId, name) { if (!confirm(`${name} aus der Gilde entfernen?`)) return; try { const res = await fetch(`/api/gildenhalle/member/${userId}/kick`, { method: 'POST' }); const d = await res.json(); if (!res.ok) { alert(d.error||'Fehler'); return; } ghShowToast(`✔ ${name} wurde entfernt`); ghLoadMyGuild(); } catch {} }; /* ── Anfrage annehmen/ablehnen ──────────────────────────── */ window.ghHandleRequest = async function(requestId, action) { try { const res = await fetch(`/api/gildenhalle/requests/${requestId}/${action}`, { method: 'POST' }); const d = await res.json(); if (!res.ok) { alert(d.error||'Fehler'); return; } ghShowToast(action === 'accept' ? '✔ Anfrage angenommen' : '✕ Anfrage abgelehnt'); ghLoadMyGuild(); } catch {} }; /* ── Gilde verlassen ────────────────────────────────────── */ window.ghLeaveGuild = async function() { if (!confirm('Wirklich die Gilde verlassen?')) return; try { const res = await fetch('/api/gildenhalle/leave', { method: 'POST' }); const d = await res.json(); if (!res.ok) { alert(d.error||'Fehler'); return; } ghShowToast('Gilde verlassen'); ghLoadMyGuild(); gh_page = 1; ghLoadGuildList(); } catch {} }; /* ════════════════════════════════════════════ PANEL 3: Aufgaben ════════════════════════════════════════════ */ async function ghLoadTasks() { const container = document.getElementById('gh-tasks-content'); if (!container) return; container.innerHTML = '
Lade Aufgaben…
'; try { const res = await fetch('/api/gildenhalle/tasks'); if (res.status === 400) { container.innerHTML = `
📋
Tritt einer Gilde bei um gemeinsame Aufgaben zu sehen.
`; return; } const data = await res.json(); if (!data.tasks?.length) { container.innerHTML = '
Keine Aufgaben für heute.
'; return; } const rewardIcons = { gold: '🪙', gems: '💎', wood: '🪵', iron: '⚙️' }; container.innerHTML = `
Gemeinsame Aufgaben der Gilde ${data.guildName}
${data.tasks.map(t => { const pct = Math.min(100, Math.round((t.current_amount / t.target_amount) * 100)); const done = t.completed || t.current_amount >= t.target_amount; const icon = rewardIcons[t.reward_type] || '🪙'; return `
${t.label} ${done ? `✔ Erledigt` : `${icon} ${t.reward_amount} ${t.reward_type}`}
${t.current_amount} / ${t.target_amount} ${pct}% · Mein Beitrag: ${t.my_contribution || 0}
`; }).join('')}
`; } catch (err) { container.innerHTML = '
Fehler beim Laden.
'; console.error('[GH/tasks]', err); } } /* ── Pagination ─────────────────────────────────────────── */ function ghRenderPagination(el, totalPages, total) { if (!el || !totalPages || totalPages <= 1) return; el.innerHTML = ` ${Array.from({length:totalPages},(_,i)=>i+1).map(p => `` ).join('')} `; el.querySelector('#gh-prev')?.addEventListener('click', () => { if(gh_page>1){gh_page--;ghLoadGuildList();} }); el.querySelector('#gh-next')?.addEventListener('click', () => { if(gh_page btn.addEventListener('click', () => { gh_page=parseInt(btn.dataset.page); ghLoadGuildList(); }) ); } /* ── Toast ──────────────────────────────────────────────── */ function ghShowToast(msg) { const t = document.createElement('div'); t.className = 'baz-toast'; t.textContent = msg; document.body.appendChild(t); setTimeout(() => t.remove(), 2800); } /* ── Schließen ──────────────────────────────────────────── */ function ghClose() { document.getElementById('gildenhalle-popup')?.classList.remove('active'); document.getElementById('qm-overlay')?.classList.remove('active'); } /* ════════════════════════════════════════════ EXPORT ════════════════════════════════════════════ */ export function loadGildenhalle() { ghLoadCSS(); ghEnsurePopup(); const popup = document.getElementById('gildenhalle-popup'); const overlay = document.getElementById('qm-overlay'); popup.style.left = '50%'; popup.style.top = '50%'; popup.style.transform = 'translate(-50%,-50%) scale(1)'; popup.classList.add('active'); overlay?.classList.add('active'); if (!gh_initialized) { gh_initialized = true; ghLoadGuildList(); } }