Stylized member cards with static fields

This commit is contained in:
2025-11-27 02:44:52 -05:00
parent 317442b8ea
commit 7cf882959b
9 changed files with 435 additions and 165 deletions

View File

@@ -1,36 +1,158 @@
async function updateEnemy() {
const factionId = parseInt(document.getElementById("enemyId").value);
const interval = parseInt(document.getElementById("refreshInterval").value);
// dashboard.js
if (!factionId || !interval) {
alert("Please enter Enemy Faction ID and Refresh Interval!");
return;
}
const friendlyMembers = new Map();
const enemyMembers = new Map();
await fetch(`/api/update_enemy_faction`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ faction_id: factionId, interval: interval })
const friendlyContainer = document.getElementById("friendly-container");
const enemyContainer = document.getElementById("enemy-container");
function createMemberCard(member) {
const card = document.createElement("div");
card.classList.add("member-card");
card.dataset.id = member.id;
// Clear innerHTML, create structured divs
const nameDiv = document.createElement("div");
nameDiv.classList.add("name");
nameDiv.textContent = member.name;
const statsDiv = document.createElement("div");
statsDiv.classList.add("stats");
statsDiv.innerHTML = `
Level: ${member.level}<br>
Estimate: ${member.estimate}<br>
Status: <span class="status-text">${member.status || "Unknown"}</span>
`;
card.appendChild(nameDiv);
card.appendChild(statsDiv);
// Store reference to DOM element
member.domElement = card;
// Make card draggable
card.draggable = true;
card.addEventListener("dragstart", e => {
e.dataTransfer.setData("text/plain", member.id);
});
// Set initial status color
updateStatusColor(member);
return card;
}
async function updateFriendly() {
const factionId = parseInt(document.getElementById("friendlyId").value);
function updateStatusColor(member) {
const statusSpan = member.domElement.querySelector(".status-text");
statusSpan.classList.remove("status-ok", "status-traveling", "status-hospitalized");
if (!factionId) {
alert("Please enter Friendly Faction ID!");
return;
}
if (!member.status) return;
const interval = parseInt(document.getElementById("refreshInterval").value);
if (!interval) {
alert("Please enter Refresh Interval!");
return;
}
await fetch(`/api/update_friendly_faction`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ faction_id: factionId, interval: interval })
});
const s = member.status.toLowerCase();
if (s === "okay") statusSpan.classList.add("status-ok");
else if (s === "traveling" || s === "abroad") statusSpan.classList.add("status-traveling");
else if (s === "hospitalized") statusSpan.classList.add("status-hospitalized");
}
async function loadMembers(faction) {
const url = faction === "friendly" ? "/api/friendly_members" : "/api/enemy_members";
const response = await fetch(url);
const members = await response.json();
const container = faction === "friendly" ? friendlyContainer : enemyContainer;
const map = faction === "friendly" ? friendlyMembers : enemyMembers;
container.innerHTML = "";
map.clear();
members.forEach(m => {
if (!m.status) m.status = "Unknown";
const card = createMemberCard(m);
map.set(m.id, m);
container.appendChild(card);
});
refreshStatus(faction);
}
async function refreshStatus(faction) {
const url = faction === "friendly" ? "/api/friendly_status" : "/api/enemy_status";
const map = faction === "friendly" ? friendlyMembers : enemyMembers;
try {
const response = await fetch(url);
const statusData = await response.json();
Object.keys(statusData).forEach(id => {
const member = map.get(parseInt(id));
if (!member) return;
member.status = statusData[id].status;
// Update DOM
const statusSpan = member.domElement.querySelector(".status-text");
statusSpan.textContent = member.status;
// Apply correct color class
updateStatusColor(member);
});
} catch (err) {
console.error("Failed to refresh status:", err);
}
}
async function populateFriendly() {
const id = parseInt(document.getElementById("friendly-id").value);
if (!id) return alert("Enter a valid faction ID!");
await fetch("/api/populate_friendly", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval: 0 })
});
loadMembers("friendly");
}
async function populateEnemy() {
const id = parseInt(document.getElementById("enemy-id").value);
if (!id) return alert("Enter a valid faction ID!");
await fetch("/api/populate_enemy", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval: 0 })
});
loadMembers("enemy");
}
async function startFriendlyStatus() {
const id = parseInt(document.getElementById("friendly-id").value);
const interval = parseInt(document.getElementById("refresh-interval").value) || 10;
await fetch("/api/start_friendly_status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval })
});
setInterval(() => refreshStatus("friendly"), interval * 1000);
}
async function startEnemyStatus() {
const id = parseInt(document.getElementById("enemy-id").value);
const interval = parseInt(document.getElementById("refresh-interval").value) || 10;
await fetch("/api/start_enemy_status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ faction_id: id, interval })
});
setInterval(() => refreshStatus("enemy"), interval * 1000);
}
document.getElementById("friendly-populate-btn").addEventListener("click", populateFriendly);
document.getElementById("enemy-populate-btn").addEventListener("click", populateEnemy);
document.getElementById("friendly-status-btn").addEventListener("click", startFriendlyStatus);
document.getElementById("enemy-status-btn").addEventListener("click", startEnemyStatus);

View File

@@ -13,7 +13,6 @@ body {
margin: 2rem auto;
}
/* Top Bar with Title + Interval Box */
.top-bar {
display: flex;
justify-content: space-between;
@@ -41,7 +40,6 @@ body {
border: none;
}
/* Horizontal Faction Row */
.faction-row {
display: flex;
flex-direction: row !important;
@@ -49,7 +47,6 @@ body {
gap: 2rem;
}
/* Each Faction Card */
.faction-card {
flex: 1;
background-color: #2c2c3e;
@@ -87,3 +84,89 @@ button {
button:hover {
background-color: #3399ff;
}
.member-list {
margin-top: 1rem;
max-height: 350px;
overflow-y: auto;
background: #1a1a26;
padding: 0.8rem;
border-radius: 10px;
}
.member-card {
background-color: #3a3a4d;
padding: 1rem;
margin: 0.5rem 0;
border-radius: 10px;
display: flex;
flex-direction: row; /* horizontal layout */
align-items: center;
justify-content: flex-start;
gap: 2rem;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
cursor: grab;
min-width: 200px;
}
.member-card:active {
cursor: grabbing;
}
/* Name section */
.member-card .name {
font-weight: bold;
color: #66ccff;
min-width: 120px; /* ensures spacing */
}
/* Stats section */
.member-card .stats {
font-size: 0.9rem;
line-height: 1.3;
color: #f0f0f0;
}
.member-card strong {
font-size: 1rem;
min-width: 100px;
}
.member-card span {
font-size: 0.9rem;
}
.member-card:hover {
background-color: #4a4a60;
}
#friendly-container,
#enemy-container {
max-height: 400px;
overflow-y: auto;
padding: 0.5rem;
border: 1px solid #444;
border-radius: 10px;
background-color: #2c2c3e;
}
#friendly-container::-webkit-scrollbar,
#enemy-container::-webkit-scrollbar {
width: 8px;
}
#friendly-container::-webkit-scrollbar-thumb,
#enemy-container::-webkit-scrollbar-thumb {
background-color: #66ccff;
border-radius: 4px;
}
#friendly-container::-webkit-scrollbar-track,
#enemy-container::-webkit-scrollbar-track {
background-color: #2c2c3e;
border-radius: 4px;
}
.status-ok { color: #28a745; font-weight: bold; }
.status-traveling { color: #3399ff; font-weight: bold; }
.status-hospitalized { color: #ff4d4d; font-weight: bold; }