127 lines
4.8 KiB
JavaScript
127 lines
4.8 KiB
JavaScript
// 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);
|