// Profile page: public hero profile lookup via ?name= query param. const ITEM_EMOJI = { weapon: 'โš”๏ธ', armor: '๐Ÿ›ก๏ธ', trinket: '๐Ÿ’', consumable: '๐Ÿงช', material: '๐Ÿ“ฆ' }; // โ”€โ”€โ”€ API helper (no auth needed) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ async function apiGet(path) { const res = await fetch(path); return res.json(); } // โ”€โ”€โ”€ Display helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function formatKd(kills, deaths) { if (deaths === 0) return kills > 0 ? `${kills}.00` : 'โ€”'; return (kills / deaths).toFixed(2); } // โ”€โ”€โ”€ Render: profile โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function renderProfile(data) { document.getElementById('profile-loading').style.display = 'none'; document.getElementById('profile-content').style.display = ''; document.getElementById('profile-name').textContent = data.name; const classEl = document.getElementById('profile-class'); classEl.textContent = data.class; classEl.className = `profile-class-badge rarity-${data.class}`; document.getElementById('profile-level').textContent = `Lv.${data.level}`; document.getElementById('profile-gold').textContent = `๐Ÿ’ฐ ${data.gold.toLocaleString()}`; document.getElementById('profile-kills').textContent = data.kills; document.getElementById('profile-deaths').textContent = data.deaths; document.getElementById('profile-kd').textContent = formatKd(data.kills, data.deaths); // Stats const statsList = document.getElementById('profile-stats-list'); statsList.innerHTML = ''; for (const stat of ['str', 'agi', 'int', 'vit']) { const row = document.createElement('div'); row.className = 'profile-stat-row'; const label = document.createElement('span'); label.className = 'profile-stat-label'; label.textContent = stat; const value = document.createElement('span'); value.className = 'profile-stat-value'; value.textContent = data[stat]; row.appendChild(label); row.appendChild(value); statsList.appendChild(row); } // Equipment const equipList = document.getElementById('profile-equipment-list'); equipList.innerHTML = ''; for (const slot of ['weapon', 'armor', 'trinket']) { const slotEl = document.createElement('div'); slotEl.className = 'profile-equip-slot'; const label = document.createElement('span'); label.className = 'profile-slot-label'; label.textContent = slot; slotEl.appendChild(label); const item = data.equipment[slot]; if (item) { const value = document.createElement('span'); value.className = `profile-slot-item rarity-${getRarityForSlot(slot, data)}`; value.textContent = `${ITEM_EMOJI[slot] || ''} ${item}`; slotEl.appendChild(value); } else { const empty = document.createElement('span'); empty.className = 'profile-slot-empty'; empty.textContent = 'Empty'; slotEl.appendChild(empty); } equipList.appendChild(slotEl); } } function getRarityForSlot(slot, data) { // Fallback: use class name for colour when item name is present but we don't have rarity info. return data.class || 'common'; } // โ”€โ”€โ”€ Init โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ async function init() { const params = new URLSearchParams(window.location.search); const name = params.get('name'); if (!name) { document.getElementById('profile-loading').style.display = 'none'; document.getElementById('profile-error').style.display = ''; document.getElementById('profile-error').textContent = 'No hero name provided. Use ?name=HeroName.'; return; } try { const data = await apiGet(`/api/heroes/${encodeURIComponent(name)}`); if (!data) { throw new Error('Empty response'); } if (!data.ok) { if (data.error?.code === 'NOT_FOUND') { document.getElementById('profile-loading').style.display = 'none'; document.getElementById('profile-notfound').style.display = ''; } else { throw new Error(data.error?.message || 'Failed to load profile.'); } return; } renderProfile(data.data); } catch (err) { document.getElementById('profile-loading').style.display = 'none'; document.getElementById('profile-error').style.display = ''; document.getElementById('profile-error').textContent = err.message || 'Failed to load profile.'; } } document.addEventListener('DOMContentLoaded', init);