Changed populate to also update status of members
This commit is contained in:
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user