/* ============================================================
public/js/buildings/gildenhalle.js
============================================================ */
let gh_initialized = false;
let gh_page = 1;
let gh_search = '';
let gh_playerGuild = null;
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 = `
`;
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 */
document.getElementById('gh-create-toggle-btn').addEventListener('click', () => {
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;
if (!data.guilds.length) {
grid.innerHTML = 'Keine Gilden gefunden.
';
return;
}
grid.innerHTML = data.guilds.map(g => `
${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
| Name | Level | Rang | Beigetreten | |
${membersHtml}
`;
} 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.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();
}
}