primo commit

This commit is contained in:
2026-09-23 18:59:27 +02:00
parent 029e9f67fb
commit bd900099a0
451 changed files with 143273 additions and 2 deletions
+548
View File
@@ -0,0 +1,548 @@
/* OGame Galaxy Viewer v2 — logica frontend */
"use strict";
const $ = (id) => document.getElementById(id);
const state = { galaxy: 1, system: 1, maxGalaxy: 0, maxSystem: 0, boot: null };
/* (utility, avatar e stelle ora in common.js) */
/* ---------------- bootstrap ---------------- */
async function loadBootstrap() {
try {
const b = await api("/api/bootstrap");
state.boot = b;
state.maxGalaxy = b.server.galaxies;
state.maxSystem = b.server.systems;
$("universe-name").textContent = b.server.name;
$("universe-sub").textContent =
`[s${b.server.number}-${b.server.community}] · velocità ${b.server.speed}x · v${b.server.version}`;
$("max-galaxy").textContent = b.server.galaxies;
$("max-system").textContent = b.server.systems;
$("meta-data-age").textContent = b.fetched.players
? `Dati: ${b.fetched.players} UTC` : "";
$("meta-reports").innerHTML =
`Spie archiviate: <b>${b.reportStats.count}</b> su ${b.reportStats.planets} pianeti`;
} catch (e) {
setMsg("err", "Impossibile contattare il server: " + e.message);
}
}
/* ---------------- galassia ---------------- */
async function loadGalaxy() {
$("nav-status").textContent = `Caricamento G${state.galaxy}:S${state.system}…`;
try {
const data = await api(`/api/galaxy?g=${state.galaxy}&s=${state.system}`);
renderGalaxy(data);
$("nav-status").textContent = "";
$("inp-galaxy").value = state.galaxy;
$("inp-system").value = state.system;
} catch (e) {
setMsg("err", e.message);
$("nav-status").textContent = "";
}
}
const stClass = (s) => "st-" + (s || "");
function renderGalaxy(data) {
const body = $("galaxy-body");
body.innerHTML = "";
let any = false;
for (const cell of data.cells) {
const tr = document.createElement("tr");
let td = document.createElement("td");
td.className = "c-pos";
td.textContent = cell.pos;
tr.appendChild(td);
td = document.createElement("td");
if (cell.planet) {
any = true;
const mine = cell.planet.mine ? " planet-mine" : "";
const ro = cell.planet.report_only;
const roTag = ro
? ` <span class="ro-tag" title="Noto solo da un rapporto di spionaggio">da rapporto</span>` : "";
const ptitle = ro ? "Pianeta noto solo da un rapporto di spionaggio" : "Apri dettagli e rapporti";
td.innerHTML =
`<div class="pcell${mine}" title="${ptitle}">` +
`<span class="p-avatar">${artSVG(cell.planet.coords, 26)}</span>` +
`<span><span class="pname">${esc(cell.planet.name)}</span><br>` +
`<span class="pcoords">${esc(cell.planet.coords)}</span>${roTag}</span></div>`;
td.querySelector(".pcell").onclick = () => openPlanet(cell.pos);
}
tr.appendChild(td);
td = document.createElement("td");
if (cell.moon) {
td.innerHTML = `<span class="mcell">${artSVG(cell.planet.coords, 22, true)}` +
`<span>${Number(cell.moon.size).toLocaleString("it")}</span></span>`;
td.querySelector(".mcell").onclick = () => openPlanet(cell.pos, "moon");
}
tr.appendChild(td);
td = document.createElement("td");
if (cell.planet && cell.player) {
const p = cell.player;
const g = p.rank_total ?? "–";
const m = p.rank_military ?? "–";
td.innerHTML =
`<span class="pname-status player-link ${stClass(p.status)}" ` +
`title="Tutti i pianeti e i rapporti di questo giocatore">${esc(p.name)}</span>` +
`<span class="pranks" title="Posizioni in classifica — Generale: #${g} · Armamenti: #${m}">` +
`<span class="rk">${icon("trophy", 11)} ${g}</span><span class="rk">${icon("swords", 11)} ${m}</span></span>`;
const pn = td.querySelector(".player-link");
if (pn) pn.onclick = () => openPlayer(p.id);
}
tr.appendChild(td);
td = document.createElement("td");
if (cell.player && cell.player.alliance_tag) {
td.innerHTML = `<span class="ally-tag">[${esc(cell.player.alliance_tag)}]</span>`;
}
tr.appendChild(td);
td = document.createElement("td");
td.className = "c-rep";
if (cell.reports && cell.reports.length) {
const n = cell.reports.length;
const hasMoon = cell.reports.some((r) => r.moon);
td.innerHTML = `<span class="rep-badge${hasMoon ? " rep-moon" : ""}" ` +
`title="${n} rapporto/i di spionaggio">${icon("badge_spy", 12)} ${n}</span>`;
td.querySelector(".rep-badge").onclick = () => openPlanet(cell.pos);
}
tr.appendChild(td);
body.appendChild(tr);
}
$("empty-state").classList.toggle("hidden", any);
}
/* ---------------- popup: pianeta ---------------- */
function popupHead(avatar, title, sub) {
$("popup-avatar").innerHTML = avatar;
$("popup-title").textContent = title;
$("popup-sub").textContent = sub;
}
async function openPlanet(pos, focusMoon) {
return openPlanetAt(`${state.galaxy}:${state.system}:${pos}`, focusMoon);
}
// Apre il pianeta date le coordinate `g:s:p` (può essere in un altro sistema,
// senza spostare la vista della galassia).
async function openPlanetAt(coords, focusMoon) {
const [g, s, p] = String(coords).split(":").map(Number);
_lastPlanetCoords = `${g}:${s}:${p}`;
try {
const data = await api(`/api/planet?g=${g}&s=${s}&p=${p}`);
renderPlanetPopup(data, focusMoon);
$("overlay").classList.remove("hidden");
} catch (e) {
setMsg("err", e.message);
}
}
// Apre la finestra giocatore: tutti i pianeti + tutti i rapporti.
async function openPlayer(pid) {
try {
const d = await api(`/api/player?id=${encodeURIComponent(pid)}`);
renderPlayerPopup(d);
$("overlay").classList.remove("hidden");
} catch (e) {
setMsg("err", e.message);
}
}
const STATUS_LABEL = { "": "attivo", i: "inattivo", v: "in vacanza", vi: "vacanza + inattivo", a: "staff" };
const CAT_ICON = {
buildings: "cat_buildings", lfbuildings: "cat_lf",
research: "cat_research", ships: "cat_ships", defense: "cat_defense",
};
const ITEM_ICON = {
buildings: { 1: "b_1", 2: "b_2", 3: "b_3", 4: "b_4", 12: "b_12", 14: "b_14", 15: "b_15",
21: "b_21", 22: "b_22", 23: "b_23", 24: "b_24", 31: "b_31", 33: "b_33",
34: "b_34", 36: "b_36", 41: "b_41", 42: "b_42", 43: "b_43", 44: "b_44" },
research: { 106: "r_106", 108: "r_108", 109: "r_109", 110: "r_110", 111: "r_111",
113: "r_113", 114: "r_114", 115: "r_115", 117: "r_117", 118: "r_118",
120: "r_120", 121: "r_121", 122: "r_122", 123: "r_123", 124: "r_124",
199: "r_199" },
ships: { 202: "ship_202", 203: "ship_203", 204: "ship_204", 205: "ship_205",
206: "ship_206", 207: "ship_207", 208: "ship_208", 209: "ship_209",
210: "ship_210", 211: "ship_211", 212: "ship_212", 213: "ship_213",
214: "ship_214", 215: "ship_215", 217: "ship_217", 218: "ship_218", 219: "ship_219" },
defense: { 401: "d_401", 402: "d_402", 403: "d_403", 404: "d_404", 405: "d_405",
406: "d_406", 407: "d_407", 408: "d_408", 502: "d_502", 503: "d_503" },
lfbuildings: { 11101: "lf_11101", 11102: "lf_11102" },
};
function itemIconName(cat, id) {
const t = ITEM_ICON[cat] || {};
return t[id] || CAT_ICON[cat] || "cat_ships";
}
function renderPlanetPopup(data, focusMoon) {
const pl = data.planet, player = data.player;
_lastPlanet = data;
_lastPlayer = null;
const mine = player.id === String(state.boot.myPlayer.id) ? " (tu)" : "";
const coords = pl.coords;
const hasMoon = !!(pl.moon);
popupHead(artSVG(coords, 52), `${pl.name} [${coords}]`, "Pianeta · clicca un rapporto per i dettagli");
let h = "";
h += `<div class="kv">`;
h += `<span class="chip"><span class="k">Giocatore</span> ` +
`<span class="v ${stClass(player.status)}">${esc(player.name)}</span>${mine}</span>`;
h += `<span class="chip"><span class="k">Stato</span> <span class="v">${STATUS_LABEL[player.status] || "—"}</span></span>`;
if (player.rank_total || player.rank_military) {
h += `<span class="chip"><span class="k">Classifica</span> <span class="v">` +
`${icon("trophy", 13)} #${player.rank_total ?? "—"} · ${icon("swords", 13)} #${player.rank_military ?? "—"}</span></span>`;
}
if (player.alliance_tag) {
h += `<span class="chip"><span class="k">Alleanza</span> ` +
`<span class="v">[${esc(player.alliance_tag)}]</span> ${esc(player.alliance_name || "")}</span>`;
}
if (hasMoon) {
h += `<span class="chip"><span class="k">Luna</span> <span class="v">` +
`${esc(pl.moon.name)} · ${Number(pl.moon.size).toLocaleString("it")} km</span></span>`;
}
h += `<span class="chip"><span class="k">Pianeti</span> <span class="v">${1 + (data.other_planets || []).length}</span></span>`;
if (pl.report_only) {
h += `<span class="chip"><span class="k">Origine</span> <span class="v">solo da rapporto</span></span>`;
}
h += `</div>`;
if (data.other_planets && data.other_planets.length) {
h += `<div class="sec"><h4>Altri pianeti di ${esc(player.name)}</h4><div class="kv">`;
h += data.other_planets.map((o) =>
`<span class="chip chip-other" data-go="${esc(o.coords)}">${artSVG(o.coords, 17)}` +
`${esc(o.name)} [${esc(o.coords)}]${o.moon ? " " + artSVG(o.coords, 13, true) : ""}</span>`).join("");
h += `</div></div>`;
}
const rpt = (list, moon) =>
`<div class="sec"><h4>${moon ? "Spionaggi sulla luna" : "Spionaggi sul pianeta"}` +
` <span class="count-pill">${list.length}</span></h4>` +
(list.length
? `<div class="rpt-grid">${list.map(rptCard).join("")}</div>`
: `<div class="empty-note">${moon
? "La luna non è mai stata spiata."
: "Nessun rapporto salvato. Spia in gioco e incolla l'API string qui sopra."}</div>`) +
`</div>`;
// sezione pianeta: sempre visibile (lista o invito a importare)
h += rpt(data.reports, false);
// sezione luna: solo se esiste una luna o ci sono spionaggi sulla luna
if (hasMoon || data.moon_reports.length) h += rpt(data.moon_reports, true);
$("popup-body").innerHTML = h;
$("popup-body").querySelectorAll(".chip-other").forEach((c) => {
c.onclick = () => { closePopup(); goToCoords(c.dataset.go); };
});
$("popup-body").querySelectorAll(".rpt-card").forEach((c) => {
c.onclick = () => openReport(c.dataset.sr);
});
if (focusMoon === "moon") {
const secs = $("popup-body").querySelectorAll(".sec h4");
for (const s of secs) if (s.textContent.includes("luna")) { s.scrollIntoView({ block: "start" }); break; }
}
}
/* ---------------- popup: giocatore ---------------- */
function renderPlayerPopup(d) {
const player = d.player, planets = d.planets || [], reports = d.reports || [];
_lastPlayer = d;
_lastPlanet = null;
const mine = String(player.id) === String(state.boot.myPlayer.id) ? " (tu)" : "";
const avatar = d.avatar || (planets[0] && planets[0].coords) || (player.name + player.id);
popupHead(artSVG(avatar, 52), player.name + mine,
`${planets.length} pianeti · ${reports.length} rapporti di spionaggio`);
let h = "";
h += `<div class="kv">`;
h += `<span class="chip"><span class="k">Stato</span> <span class="v ${stClass(player.status)}">` +
`${STATUS_LABEL[player.status] || "—"}</span></span>`;
if (player.rank_total || player.rank_military) {
h += `<span class="chip"><span class="k">Classifica</span> <span class="v">` +
`${icon("trophy", 13)} #${player.rank_total ?? "—"} · ${icon("swords", 13)} #${player.rank_military ?? "—"}</span></span>`;
}
if (player.alliance_tag) {
h += `<span class="chip"><span class="k">Alleanza</span> ` +
`<span class="v">[${esc(player.alliance_tag)}]</span> ${esc(player.alliance_name || "")}</span>`;
}
h += `<span class="chip"><span class="k">Spie</span> <span class="v">${reports.length}</span></span>`;
h += `</div>`;
// pianeti del giocatore
h += `<div class="sec"><h4>Pianeti <span class="count-pill">${planets.length}</span></h4>`;
h += `<div class="kv">` + planets.map((o) =>
`<span class="chip chip-other" data-coords="${esc(o.coords)}">${artSVG(o.coords, 17)}` +
`${esc(o.name)} [${esc(o.coords)}]${o.moon ? " " + artSVG(o.coords, 13, true) : ""}</span>`
).join("") + `</div></div>`;
// tutti i rapporti del giocatore (pianeti e lune)
h += `<div class="sec"><h4>Rapporti di spionaggio <span class="count-pill">${reports.length}</span></h4>` +
(reports.length
? `<div class="rpt-grid">${reports.map(rptCard).join("")}</div>`
: `<div class="empty-note">Nessun rapporto salvato su questo giocatore.</div>`) +
`</div>`;
$("popup-body").innerHTML = h;
$("popup-body").querySelectorAll(".chip-other").forEach((c) => {
c.onclick = () => openPlanetAt(c.dataset.coords);
});
$("popup-body").querySelectorAll(".rpt-card").forEach((c) => {
c.onclick = () => openReport(c.dataset.sr);
});
}
/* ---------------- card rapporto ---------------- */
function actClass(a) {
if (a === null || a === undefined) return "act-none";
if (a <= 0) return "act-0";
if (a <= 15) return "act-15";
if (a <= 30) return "act-30";
return "act-60";
}
function actLabel(a) {
if (a === null || a === undefined) return "attività sconosciuta";
if (a <= 0) return "inattivo ≥7gg";
return `attivo ≤ ${a} min`;
}
function fmtEv(evt) {
if (!evt) return "";
const d = new Date(evt);
return isNaN(d) ? evt : d.toLocaleString("it-IT", { dateStyle: "medium", timeStyle: "short" });
}
function rptCard(rec) {
const c = rec.categories || {};
const loot = rec.loot_percentage !== null && rec.loot_percentage !== undefined;
const parts = [];
if (c.ships) parts.push(`${icon("cat_ships", 13)} ${c.ships}`);
if (c.defense) parts.push(`${icon("cat_defense", 13)} ${c.defense}`);
if (c.buildings) parts.push(`${icon("cat_buildings", 13)} ${c.buildings}`);
if (c.research) parts.push(`${icon("cat_research", 13)} ${c.research}`);
return `<div class="rpt-card" data-sr="${esc(rec.sr_id)}">` +
`<div class="top"><span class="act-dot ${actClass(rec.activity)}"></span>` +
`<span class="rpt-date">${fmtEv(rec.event_time)}</span>` +
`<span class="rpt-kind ${rec.moon ? "moon" : "planet"}">${rec.moon ? "luna" : "pianeta"}</span></div>` +
`<div class="rpt-body"><div class="rpt-target">${esc(rec.defender_planet_name || "?")}` +
(rec.coords ? ` <span class="rpt-coords">[${esc(rec.coords)}]</span>` : "") +
`</div><div class="rpt-from">da ${esc(rec.attacker_name || "?")}` +
(loot ? ` · bottino ${rec.loot_percentage}%` : "") + `</div></div>` +
`<div class="rpt-foot"><span class="meta-dots">${parts.map((p) => `<span class="m">${p}</span>`).join("") || "—"}</span>` +
`<span class="rpt-open">Apri ›</span></div></div>`;
}
/* ---------------- dettaglio rapporto ---------------- */
let _lastPlanet = null;
let _lastPlanetCoords = null;
let _lastPlayer = null;
async function openReport(srId) {
try {
const d = await api(`/api/reports/${srId}`);
renderReportDetail(d.meta, d.data);
} catch (e) {
setMsg("err", e.message);
}
}
async function deleteReport(srId) {
if (!confirm("Eliminare definitivamente questo rapporto di spionaggio?")) return;
try {
await api(`/api/reports/${srId}`, { method: "DELETE" });
if (_lastPlanetCoords) {
await openPlanetAt(_lastPlanetCoords); // ricarica il pianeta: lista aggiornata
} else if (_lastPlayer && _lastPlayer.player) {
await openPlayer(_lastPlayer.player.id); // ricarica il giocatore: lista aggiornata
} else {
closePopup();
}
loadGalaxy(); // aggiorna i badge "spie" nella galassia
} catch (e) {
setMsg("err", "Eliminazione non riuscita: " + e.message);
}
}
function renderReportDetail(meta, rep) {
const gen = rep.generic || {}, det = rep.details || {}, res = det.resources || {};
const coords = meta.coords || gen.defender_planet_coordinates || "";
popupHead(artSVG(coords, 52), `Spionaggio — ${meta.defender_planet_name || "?"}`,
`${coords}${meta.planet_type === 3 ? " (luna)" : ""} · ${fmtEv(meta.event_time)}`);
let h = "";
h += `<div class="rpt-actions">` +
`<button class="back-btn" id="btn-back">${icon("ui_chev_l", 14)} Indietro al pianeta</button>` +
`<button class="del-btn" id="btn-del">${icon("ui_trash", 14)} Elimina rapporto</button>` +
`</div>`;
h += `<div class="kv">`;
h += `<span class="chip"><span class="k">Difensore</span> <span class="v">${esc(meta.defender_name || "?")}</span></span>`;
h += `<span class="chip"><span class="k">Attaccante</span> <span class="v">${esc(gen.attacker_name || "?")}</span></span>`;
h += `<span class="chip"><span class="k">Attività</span> <span class="v">${actLabel(meta.activity)}</span></span>`;
if (meta.loot_percentage !== null && meta.loot_percentage !== undefined)
h += `<span class="chip"><span class="k">Bottino</span> <span class="v">${meta.loot_percentage}%</span></span>`;
if (gen.spy_fail_chance !== undefined)
h += `<span class="chip"><span class="k">Rischio fall.</span> <span class="v">${gen.spy_fail_chance}%</span></span>`;
if (gen.debris_metal || gen.debris_crystal)
h += `<span class="chip"><span class="k">Campo detriti</span> <span class="v">${icon("extra_debris", 13)} ${num(gen.debris_metal)} / ${num(gen.debris_crystal)}</span></span>`;
h += `</div>`;
const rt = (lab, ico, val, cls) =>
`<div class="res-tile ${cls}"><span class="r-ico">${ico}</span><span>` +
`<div class="r-lab">${lab}</div><div class="r-val">${val}</div></span></div>`;
h += `<div class="res-grid">`;
h += rt("Metallo", icon("res_metal", 20), num(res.metal), "res-metal");
h += rt("Cristallo", icon("res_crystal", 20), num(res.crystal), "res-crystal");
h += rt("Deuterio", icon("res_deuterium", 20), num(res.deuterium), "res-deuterium");
if (res.population) h += rt("Popolazione", icon("res_pop", 20), num(res.population), "res-pop");
if (res.food) h += rt("Cibo", icon("res_food", 20), num(res.food), "res-food");
h += `</div>`;
const failed = Object.entries({
"edifici": gen.failed_buildings, "ricerca": gen.failed_research,
"navi": gen.failed_ships, "difese": gen.failed_defense,
}).filter(([, v]) => v).map(([k]) => k);
if (failed.length)
h += `<div class="warn">${icon("ui_warn", 14)} Spionaggio fallito su: ${failed.join(", ")} — dati incompleti.</div>`;
h += techSection("Edifici", "buildings", det.buildings, (b) => [b.building_type, `L. ${b.level}`], "liv.");
h += techSection("Edifici Lifeform", "lfbuildings", det.lfbuildings, (b) => [b.lfbuilding_type, `L. ${b.level}`], "liv.");
h += techSection("Ricerca", "research", det.research, (b) => [b.research_type, `L. ${b.level}`], "liv.");
h += techSection("Navi", "ships", det.ships, (b) => [b.ship_type, num(b.count)], "unità");
h += techSection("Difese", "defense", det.defense, (b) => [b.defense_type, num(b.count)], "unità");
$("popup-body").innerHTML = h;
$("btn-back").onclick = () => {
if (_lastPlanet) renderPlanetPopup(_lastPlanet);
else if (_lastPlayer) renderPlayerPopup(_lastPlayer);
else closePopup();
};
$("btn-del").onclick = () => deleteReport(meta.sr_id);
}
function techSection(title, cat, rows, get, valLabel) {
if (!rows || !rows.length) return "";
const cards = rows.map((it) => {
const [tid, val] = get(it);
return `<div class="tcard cat-${cat}" title="${esc(itemName(cat, tid))}">` +
`<span class="tico">${icon(itemIconName(cat, tid), 19)}</span>` +
`<span class="tname">${esc(itemName(cat, tid))}</span>` +
`<span class="tval">${val}</span></div>`;
}).join("");
return `<div class="sec"><h4>${title} <span class="count-pill">${rows.length}</span></h4>` +
`<div class="tech-grid">${cards}</div></div>`;
}
/* ---------------- nomi e icone elementi ---------------- */
const NAMES = {
buildings: {
1: "Miniera di metallo", 2: "Miniera di cristallo", 3: "Sintetizzatore di deuterio",
4: "Centrale solare", 12: "Centrale a fusione", 14: "Fabbrica di robot",
15: "Fabbrica di naniti", 21: "Cantiere spaziale", 22: "Deposito di metallo",
23: "Deposito di cristallo", 24: "Serbatoio di deuterio", 31: "Laboratorio di ricerca",
33: "Terraformer", 34: "Deposito dell'alleanza", 36: "Dock di riparazione",
41: "Base lunare", 42: "Falanx sensoriale", 43: "Portale quantico", 44: "Silo missilistico",
},
research: {
106: "Tecnologia spionaggio", 108: "Tecnologia informatica", 109: "Tecnologia delle armi",
110: "Tecnologia scudi", 111: "Tecnologia corazzatura", 113: "Tecnologia energetica",
114: "Tecnologia iperspazio", 115: "Motore a combustione", 117: "Motore a impulso",
118: "Motore iperspaziale", 120: "Tecnologia laser", 121: "Tecnologia ionica",
122: "Tecnologia al plasma", 123: "Rete di ricerca intergalattica",
124: "Astrofisica", 199: "Tecnologia gravitoni",
},
ships: {
202: "Cargo leggero", 203: "Cargo pesante", 204: "Caccia leggero", 205: "Caccia pesante",
206: "Incrociatore", 207: "Nave da battaglia", 208: "Nave colonizzatrice",
209: "Riciclatore", 210: "Sonda di spionaggio", 211: "Bombardiere", 212: "Satellite solare",
213: "Corazzata", 214: "Morte Nera", 215: "Incrociatore da battaglia", 217: "Crawler",
218: "Reaper", 219: "Pathfinder",
},
defense: {
401: "Lanciamissili", 402: "Laser leggero", 403: "Laser pesante", 404: "Cannone Gauss",
405: "Cannone ionico", 406: "Cannone al plasma", 407: "Cupola piccola", 408: "Cupola grande",
502: "Missili anti-balistici", 503: "Missili interplanetari",
},
lfbuildings: { 11101: "Campo di coltivazione", 11102: "Centro di ricerca" },
};
function itemName(cat, id) {
const t = NAMES[cat] || {};
return t[id] || (cat === "lfbuildings" ? "Edificio Lifeform #" + id : cat + " #" + id);
}
/* ---------------- navigazione ---------------- */
function setSystem(s) { state.system = Math.max(1, Math.min(state.maxSystem, s)); loadGalaxy(); }
function setGalaxy(g) { state.galaxy = Math.max(1, Math.min(state.maxGalaxy, g)); loadGalaxy(); }
function closePopup() { $("overlay").classList.add("hidden"); _lastPlanet = null; _lastPlayer = null; }
function goToCoords(c) {
const [g, s] = c.split(":").map(Number);
closePopup();
state.galaxy = Math.max(1, Math.min(state.maxGalaxy, g || 1));
state.system = Math.max(1, Math.min(state.maxSystem, s || 1));
loadGalaxy();
}
/* ---------------- messaggi / import ---------------- */
function setMsg(kind, text, ms = 6000) {
const el = $("import-msg");
el.className = kind;
el.textContent = text;
if (ms) setTimeout(() => { el.textContent = ""; }, ms);
}
async function importReport() {
const token = $("inp-token").value.trim();
if (!token) { setMsg("err", "Incolla prima l'API string."); return; }
const btn = $("btn-import");
btn.disabled = true;
setMsg("ok", "Recupero del rapporto…");
try {
const res = await api("/api/reports", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
$("inp-token").value = "";
setMsg("ok",
res.created
? `Importato: ${res.report.defender_name} — ${res.coords}${res.planet_type === 3 ? " (luna)" : ""}`
: `Rapporto già in archivio (${res.coords}).`);
loadGalaxy();
} catch (e) {
setMsg("err", e.message);
} finally {
btn.disabled = false;
}
}
/* ---------------- eventi ---------------- */
$("btn-import").onclick = importReport;
$("inp-token").addEventListener("keydown", (e) => { if (e.key === "Enter") importReport(); });
$("btn-prev-sys").onclick = () => setSystem(state.system - 1);
$("btn-next-sys").onclick = () => setSystem(state.system + 1);
$("btn-refresh").onclick = () => loadGalaxy();
$("inp-system").addEventListener("change", (e) => setSystem(parseInt(e.target.value, 10) || 1));
$("inp-galaxy").addEventListener("change", (e) => setGalaxy(parseInt(e.target.value, 10) || 1));
$("popup-close").onclick = closePopup;
$("overlay").addEventListener("click", (e) => { if (e.target === $("overlay")) closePopup(); });
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && !$("overlay").classList.contains("hidden")) closePopup();
});
/* ---------------- init ---------------- */
initStars();
const il = document.getElementById("import-label");
if (il) il.innerHTML = icon("ui_import", 15) + ' <span>Nuovo rapporto</span>';
const tli = document.querySelector(".tl-ico");
if (tli) tli.innerHTML = icon("trophy", 14);
const pcl = document.getElementById("popup-close");
if (pcl) pcl.innerHTML = icon("ui_close", 16);
const _qs = new URLSearchParams(location.search);
const _g = parseInt(_qs.get("g") || "", 10);
const _s = parseInt(_qs.get("s") || "", 10);
(async () => {
await loadBootstrap();
if (_g) state.galaxy = Math.max(1, Math.min(state.maxGalaxy, _g));
if (_s) state.system = Math.max(1, Math.min(state.maxSystem, _s));
await loadGalaxy();
})();