Changed populate to also update status of members

This commit is contained in:
2025-11-28 12:28:22 -05:00
parent 4b0038a4b7
commit 788f2fec2c

View File

@@ -13,6 +13,27 @@ const enemyContainer = document.getElementById("enemy-container");
// utility to safe parse ints // utility to safe parse ints
function toInt(v) { const n = Number(v); return Number.isNaN(n) ? null : n; } function toInt(v) { const n = Number(v); return Number.isNaN(n) ? null : n; }
// ---- Apply status CSS class to the .status-text inside a member card ----
function applyStatusClass(member) {
const span = member.domElement?.querySelector(".status-text");
if (!span) return;
span.classList.remove("status-ok", "status-traveling", "status-hospitalized");
const s = String(member.status || "").toLowerCase();
if (s === "okay") span.classList.add("status-ok");
else if (s === "traveling" || s === "abroad") span.classList.add("status-traveling");
else if (s === "hospitalized" || s === "hospital") span.classList.add("status-hospitalized");
}
// ---- Decide which CSS class a status should use (helper used when creating card) ---
function statusClass(status) {
if (!status) return "";
status = status.toLowerCase();
if (status.includes("okay")) return "status-ok";
if (status.includes("travel") || status.includes("abroad")) return "status-traveling";
if (status.includes("hospital")) return "status-hospitalized";
return "";
}
// ---- Create structured card ---- // ---- Create structured card ----
function createMemberCard(member, kind) { function createMemberCard(member, kind) {
const card = document.createElement("div"); const card = document.createElement("div");
@@ -29,13 +50,15 @@ function createMemberCard(member, kind) {
const statsDiv = document.createElement("div"); const statsDiv = document.createElement("div");
statsDiv.className = "stats"; statsDiv.className = "stats";
statsDiv.innerHTML = `Lv: ${member.level} <br> Est: ${member.estimate} <br> Status: <span class="status-text">${member.status || "Unknown"}</span>`; // Ensure initial status text and class (maybe "Unknown" until refresh)
statsDiv.innerHTML = `Lv: ${member.level} <br> Est: ${member.estimate} <br> Status: <span class="status-text ${statusClass(member.status)}">${member.status || "Unknown"}</span>`;
card.appendChild(nameDiv); card.appendChild(nameDiv);
card.appendChild(statsDiv); card.appendChild(statsDiv);
member.domElement = card; member.domElement = card;
// drag payload is JSON (kind + id)
card.addEventListener("dragstart", (e) => { card.addEventListener("dragstart", (e) => {
const payload = JSON.stringify({ kind, id: member.id }); const payload = JSON.stringify({ kind, id: member.id });
e.dataTransfer.setData("text/plain", payload); e.dataTransfer.setData("text/plain", payload);
@@ -45,28 +68,21 @@ function createMemberCard(member, kind) {
card.style.opacity = "1"; card.style.opacity = "1";
}); });
// Apply initial status class (for cases where member.status was set by populate)
applyStatusClass(member); applyStatusClass(member);
return card; return card;
} }
// ---- Apply status CSS class to the .status-text inside a member card ----
function applyStatusClass(member) {
const span = member.domElement?.querySelector(".status-text");
if (!span) return;
span.classList.remove("status-ok", "status-traveling", "status-hospitalized");
const s = String(member.status || "").toLowerCase();
if (s === "okay") span.classList.add("status-ok");
else if (s === "traveling" || s === "abroad") span.classList.add("status-traveling");
else if (s === "hospitalized" || s === "hospital") span.classList.add("status-hospitalized");
}
// ---- Load static members from backend ---- // ---- Load static members from backend ----
async function loadMembers(faction) { async function loadMembers(faction) {
const url = faction === "friendly" ? "/api/friendly_members" : "/api/enemy_members"; const url = faction === "friendly" ? "/api/friendly_members" : "/api/enemy_members";
try { try {
const res = await fetch(url, { cache: "no-store" }); const res = await fetch(url, { cache: "no-store" });
if (!res.ok) return console.error("Failed to fetch members:", res.status); if (!res.ok) {
console.error("Failed to fetch members:", res.status);
return;
}
const arr = await res.json(); const arr = await res.json();
const container = faction === "friendly" ? friendlyContainer : enemyContainer; const container = faction === "friendly" ? friendlyContainer : enemyContainer;
const map = faction === "friendly" ? friendlyMembers : enemyMembers; const map = faction === "friendly" ? friendlyMembers : enemyMembers;
@@ -80,6 +96,9 @@ async function loadMembers(faction) {
map.set(m.id, m); map.set(m.id, m);
container.appendChild(card); container.appendChild(card);
}); });
// after we've added elements, ensure drop listeners are attached
setupDropZones();
} catch (err) { } catch (err) {
console.error("loadMembers error", err); console.error("loadMembers error", err);
} }
@@ -99,8 +118,10 @@ async function refreshStatus(faction) {
if (!member) return; if (!member) return;
member.status = statusData[k].status; member.status = statusData[k].status;
const span = member.domElement.querySelector(".status-text"); const span = member.domElement.querySelector(".status-text");
span.textContent = member.status; if (span) {
applyStatusClass(member); span.textContent = member.status;
applyStatusClass(member);
}
}); });
} catch (err) { } catch (err) {
console.error("refreshStatus error", err); console.error("refreshStatus error", err);
@@ -108,6 +129,7 @@ async function refreshStatus(faction) {
} }
// ---- Populate endpoints ---- // ---- Populate endpoints ----
// NOW: populate also immediately fetches the latest status snapshot and applies it.
async function populateFriendly() { async function populateFriendly() {
const id = toInt(document.getElementById("friendly-id").value); const id = toInt(document.getElementById("friendly-id").value);
if (!id) return alert("Enter friendly faction ID"); if (!id) return alert("Enter friendly faction ID");
@@ -116,7 +138,11 @@ async function populateFriendly() {
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval: 0 }) body: JSON.stringify({ faction_id: id, interval: 0 })
}); });
// Rebuild list and then immediately fetch status snapshot
await loadMembers("friendly"); await loadMembers("friendly");
// try to pull status once so the status text shows up immediately
await refreshStatus("friendly");
} }
async function populateEnemy() { async function populateEnemy() {
@@ -127,7 +153,9 @@ async function populateEnemy() {
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval: 0 }) body: JSON.stringify({ faction_id: id, interval: 0 })
}); });
await loadMembers("enemy"); await loadMembers("enemy");
await refreshStatus("enemy");
} }
// ---- Start/Stop status refresh loops ---- // ---- Start/Stop status refresh loops ----
@@ -136,6 +164,11 @@ let enemyStatusIntervalHandle = null;
async function toggleFriendlyStatus() { async function toggleFriendlyStatus() {
const btn = document.getElementById("friendly-status-btn"); const btn = document.getElementById("friendly-status-btn");
if (!btn) {
console.error("friendly-status-btn not found");
return;
}
if (friendlyStatusIntervalHandle) { if (friendlyStatusIntervalHandle) {
clearInterval(friendlyStatusIntervalHandle); clearInterval(friendlyStatusIntervalHandle);
friendlyStatusIntervalHandle = null; friendlyStatusIntervalHandle = null;
@@ -155,11 +188,17 @@ async function toggleFriendlyStatus() {
friendlyStatusIntervalHandle = setInterval(() => refreshStatus("friendly"), interval * 1000); friendlyStatusIntervalHandle = setInterval(() => refreshStatus("friendly"), interval * 1000);
btn.textContent = "Stop Refresh"; btn.textContent = "Stop Refresh";
// do an immediate status fetch as well
refreshStatus("friendly"); refreshStatus("friendly");
} }
async function toggleEnemyStatus() { async function toggleEnemyStatus() {
const btn = document.getElementById("enemy-status-btn"); const btn = document.getElementById("enemy-status-btn");
if (!btn) {
console.error("enemy-status-btn not found");
return;
}
if (enemyStatusIntervalHandle) { if (enemyStatusIntervalHandle) {
clearInterval(enemyStatusIntervalHandle); clearInterval(enemyStatusIntervalHandle);
enemyStatusIntervalHandle = null; enemyStatusIntervalHandle = null;
@@ -182,14 +221,25 @@ async function toggleEnemyStatus() {
refreshStatus("enemy"); refreshStatus("enemy");
} }
// ---- Drag & drop handling for drop-zones ---- // -------------------
// DRAG & DROP SYSTEM
// -------------------
function setupDropZones() { function setupDropZones() {
const dropZones = document.querySelectorAll(".drop-zone, .member-list"); const dropZones = document.querySelectorAll(".drop-zone, .member-list");
dropZones.forEach(zone => { dropZones.forEach(zone => {
// attach listeners only once
if (zone.dataset._drag_listeners_attached) return;
zone.addEventListener("dragover", (e) => { zone.addEventListener("dragover", (e) => {
e.preventDefault(); e.preventDefault();
const raw = e.dataTransfer.types.includes("text/plain") ? e.dataTransfer.getData("text/plain") : null; // check payload validity
let raw = null;
try {
raw = e.dataTransfer.getData("text/plain");
} catch (err) {
raw = null;
}
let valid = false; let valid = false;
if (raw) { if (raw) {
try { try {
@@ -211,7 +261,13 @@ function setupDropZones() {
zone.addEventListener("drop", (e) => { zone.addEventListener("drop", (e) => {
e.preventDefault(); e.preventDefault();
zone.classList.remove("dragover-valid", "dragover-invalid"); zone.classList.remove("dragover-valid", "dragover-invalid");
const raw = e.dataTransfer.getData("text/plain");
let raw = null;
try {
raw = e.dataTransfer.getData("text/plain");
} catch (err) {
raw = null;
}
if (!raw) return; if (!raw) return;
let payload; let payload;
try { payload = JSON.parse(raw); } catch { return; } try { payload = JSON.parse(raw); } catch { return; }
@@ -234,10 +290,14 @@ function setupDropZones() {
// persist assignment in localStorage // persist assignment in localStorage
saveAssignments(); saveAssignments();
}); });
zone.dataset._drag_listeners_attached = "1";
}); });
} }
// ---- Persist card assignments to localStorage ---- // -------------------
// localStorage helpers for battle groups (the format used earlier)
// -------------------
function saveAssignments() { function saveAssignments() {
const groups = document.querySelectorAll(".group"); const groups = document.querySelectorAll(".group");
const data = {}; const data = {};
@@ -250,8 +310,7 @@ function saveAssignments() {
localStorage.setItem("battleAssignments", JSON.stringify(data)); localStorage.setItem("battleAssignments", JSON.stringify(data));
} }
// ---- Restore card assignments from localStorage ---- function loadAssignmentsFromStorage() {
function loadAssignments() {
const data = JSON.parse(localStorage.getItem("battleAssignments") || "{}"); const data = JSON.parse(localStorage.getItem("battleAssignments") || "{}");
Object.keys(data).forEach(gid => { Object.keys(data).forEach(gid => {
const group = document.querySelector(`.group[data-id="${gid}"]`); const group = document.querySelector(`.group[data-id="${gid}"]`);
@@ -286,6 +345,13 @@ function wireUp() {
document.getElementById("enemy-populate-btn").addEventListener("click", populateEnemy); document.getElementById("enemy-populate-btn").addEventListener("click", populateEnemy);
document.getElementById("friendly-status-btn").addEventListener("click", toggleFriendlyStatus); document.getElementById("friendly-status-btn").addEventListener("click", toggleFriendlyStatus);
document.getElementById("enemy-status-btn").addEventListener("click", toggleEnemyStatus); document.getElementById("enemy-status-btn").addEventListener("click", toggleEnemyStatus);
document.getElementById("reset-groups-btn").addEventListener("click", () => {
// clear storage and put members back
localStorage.removeItem("battleAssignments");
// move all back to lists
friendlyMembers.forEach(m => { if (m.domElement) friendlyContainer.appendChild(m.domElement); });
enemyMembers.forEach(m => { if (m.domElement) enemyContainer.appendChild(m.domElement); });
});
setupDropZones(); setupDropZones();
attachCardClickLogging(); attachCardClickLogging();
@@ -296,5 +362,5 @@ document.addEventListener("DOMContentLoaded", async () => {
wireUp(); wireUp();
await loadMembers("friendly"); await loadMembers("friendly");
await loadMembers("enemy"); await loadMembers("enemy");
loadAssignments(); // restore positions after load loadAssignmentsFromStorage(); // restore positions after load
}); });