minor fix on node.html table of tackets shows to and from not just from.

This commit is contained in:
Pablo Revilla
2025-12-08 10:45:33 -08:00
parent d3bf0ede67
commit 3d6c01f020
+116 -23
View File
@@ -8,7 +8,7 @@ table {
margin: 1em auto;
}
/* Ensure table is centered visually */
/* Ensure table centered visually */
#node-list {
display: flex;
justify-content: center;
@@ -19,7 +19,6 @@ table {
margin-right: auto;
}
th, td {
padding: 10px;
border: 1px solid #333;
@@ -98,6 +97,7 @@ select, .export-btn, .search-box, .clear-btn {
color: white;
}
/* Favorite stars */
.favorite-star {
cursor: pointer;
font-size: 1.2em;
@@ -113,6 +113,7 @@ select, .export-btn, .search-box, .clear-btn {
color: #ffd700;
}
/* Favorite filter button */
.favorites-btn {
background-color: #ffd700;
color: #000;
@@ -127,6 +128,55 @@ select, .export-btn, .search-box, .clear-btn {
background-color: #ff6b6b;
color: white;
}
/* --------------------------------------------- */
/* MOBILE CARD VIEW */
/* --------------------------------------------- */
@media (max-width: 768px) {
/* Hide desktop table */
#node-list table {
display: none;
}
/* Show mobile card list */
#mobile-node-list {
display: block !important;
width: 100%;
padding: 0 10px;
}
.node-card {
background: #1e1e1e;
border: 1px solid #333;
border-radius: 8px;
padding: 12px 15px;
margin-bottom: 12px;
color: white;
}
.node-card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
font-weight: bold;
margin-bottom: 8px;
}
.node-card-field {
margin: 4px 0;
font-size: 0.9em;
}
.node-card-field b {
color: #9fd4ff;
}
.favorite-star {
font-size: 1.4em;
}
}
</style>
{% endblock %}
@@ -176,6 +226,7 @@ select, .export-btn, .search-box, .clear-btn {
<span data-translate-lang="nodes_suffix">nodes</span>
</div>
<!-- Desktop table -->
<div id="node-list">
<table>
<thead>
@@ -202,6 +253,9 @@ select, .export-btn, .search-box, .clear-btn {
</table>
</div>
<!-- Mobile Card View -->
<div id="mobile-node-list" style="display:none;"></div>
<script>
// =====================================================
// TRANSLATIONS
@@ -247,7 +301,6 @@ const keyMap = [
"last_lat","last_long","channel","last_seen_us"
];
// FAVORITES
function getFavorites() {
const favorites = localStorage.getItem('nodelist_favorites');
return favorites ? JSON.parse(favorites) : [];
@@ -266,27 +319,18 @@ function isFavorite(nodeId) {
return getFavorites().includes(nodeId);
}
// TIME AGO (translated)
function timeAgo(usTimestamp) {
if (!usTimestamp) return "N/A";
const ms = usTimestamp / 1000;
const diff = Date.now() - ms;
// < 1 minute
if (diff < 60000) return nodelistTranslations.time_just_now || "just now";
if (diff < 60000) return "just now";
const mins = Math.floor(diff / 60000);
if (mins < 60)
return `${mins} ${nodelistTranslations.time_min_ago || "min ago"}`;
if (mins < 60) return `${mins} min ago`;
const hrs = Math.floor(mins / 60);
if (hrs < 24)
return `${hrs} ${nodelistTranslations.time_hr_ago || "hr ago"}`;
if (hrs < 24) return `${hrs} hr ago`;
const days = Math.floor(hrs / 24);
if (days === 1)
return `1 ${nodelistTranslations.time_day_ago || "day ago"}`;
return `${days} ${nodelistTranslations.time_days_ago || "days ago"}`;
return `${days} days ago`;
}
// =====================================================
@@ -297,6 +341,8 @@ document.addEventListener("DOMContentLoaded", async function() {
await loadTranslationsNodelist();
const tbody = document.getElementById("node-table-body");
const mobileList = document.getElementById("mobile-node-list");
const roleFilter = document.getElementById("role-filter");
const channelFilter = document.getElementById("channel-filter");
const hwFilter = document.getElementById("hw-filter");
@@ -317,12 +363,14 @@ document.addEventListener("DOMContentLoaded", async function() {
firmware: n.firmware || n.firmware_version || ""
}));
populateFilters(allNodes);
renderTable(allNodes);
updateSortIcons();
} catch (err) {
tbody.innerHTML = `<tr><td colspan="10" style="text-align:center; color:red;">${nodelistTranslations.error_loading_nodes || "Error loading nodes"}</td></tr>`;
tbody.innerHTML = `<tr>
<td colspan="10" style="text-align:center; color:red;">
${nodelistTranslations.error_loading_nodes || "Error loading nodes"}
</td></tr>`;
}
roleFilter.addEventListener("change", applyFilters);
@@ -334,7 +382,8 @@ document.addEventListener("DOMContentLoaded", async function() {
clearBtn.addEventListener("click", clearFilters);
favoritesBtn.addEventListener("click", toggleFavoritesFilter);
tbody.addEventListener("click", e => {
// Favorite star click handler
document.addEventListener("click", e => {
if (e.target.classList.contains('favorite-star')) {
const nodeId = parseInt(e.target.dataset.nodeId);
const isFav = isFavorite(nodeId);
@@ -347,6 +396,7 @@ document.addEventListener("DOMContentLoaded", async function() {
e.target.textContent = "★";
}
toggleFavorite(nodeId);
applyFilters();
}
});
@@ -387,9 +437,8 @@ document.addEventListener("DOMContentLoaded", async function() {
function toggleFavoritesFilter() {
showOnlyFavorites = !showOnlyFavorites;
favoritesBtn.textContent = showOnlyFavorites
? (nodelistTranslations.show_all || "⭐ Show All")
: (nodelistTranslations.show_favorites || "⭐ Show Favorites");
? "Show All"
: "⭐ Show Favorites";
favoritesBtn.classList.toggle("active", showOnlyFavorites);
applyFilters();
}
@@ -421,6 +470,9 @@ document.addEventListener("DOMContentLoaded", async function() {
function renderTable(nodes) {
tbody.innerHTML = "";
mobileList.innerHTML = "";
const isMobile = window.innerWidth <= 768;
if (!nodes.length) {
tbody.innerHTML = `<tr>
@@ -428,6 +480,8 @@ document.addEventListener("DOMContentLoaded", async function() {
${nodelistTranslations.no_nodes_found || "No nodes found"}
</td>
</tr>`;
mobileList.innerHTML = `<div style="text-align:center; color:white;">No nodes found</div>`;
countSpan.textContent = 0;
return;
}
@@ -436,6 +490,7 @@ document.addEventListener("DOMContentLoaded", async function() {
const isFav = isFavorite(node.node_id);
const star = isFav ? "★" : "☆";
// DESKTOP TABLE ROW
const row = document.createElement("tr");
row.innerHTML = `
<td>${node.short_name || "N/A"}</td>
@@ -454,8 +509,44 @@ document.addEventListener("DOMContentLoaded", async function() {
</td>
`;
tbody.appendChild(row);
// MOBILE CARD VIEW
const card = document.createElement("div");
card.className = "node-card";
card.innerHTML = `
<div class="node-card-header">
<span>${node.short_name || node.long_name || node.node_id}</span>
<span class="favorite-star ${isFav ? "active" : ""}" data-node-id="${node.node_id}">
${star}
</span>
</div>
<div class="node-card-field"><b>ID:</b> ${node.node_id}</div>
<div class="node-card-field"><b>Name:</b> ${node.long_name || "N/A"}</div>
<div class="node-card-field"><b>HW:</b> ${node.hw_model || "N/A"}</div>
<div class="node-card-field"><b>Firmware:</b> ${node.firmware || "N/A"}</div>
<div class="node-card-field"><b>Role:</b> ${node.role || "N/A"}</div>
<div class="node-card-field"><b>Location:</b>
${node.last_lat ? (node.last_lat / 1e7).toFixed(5) : "N/A"},
${node.last_long ? (node.last_long / 1e7).toFixed(5) : "N/A"}
</div>
<div class="node-card-field"><b>Channel:</b> ${node.channel}</div>
<div class="node-card-field"><b>Last Seen:</b> ${timeAgo(node.last_seen_us)}</div>
<a href="/node/${node.node_id}" style="color:#9fd4ff; text-decoration:underline; margin-top:5px; display:block;">
View Node →
</a>
`;
mobileList.appendChild(card);
});
// Toggle correct view
if (isMobile) {
mobileList.style.display = "block";
} else {
mobileList.style.display = "none";
}
countSpan.textContent = nodes.length;
}
@@ -469,7 +560,7 @@ document.addEventListener("DOMContentLoaded", async function() {
sortAsc = true;
showOnlyFavorites = false;
favoritesBtn.textContent = nodelistTranslations.show_favorites || "⭐ Show Favorites";
favoritesBtn.textContent = "⭐ Show Favorites";
favoritesBtn.classList.remove("active");
renderTable(allNodes);
@@ -502,10 +593,12 @@ document.addEventListener("DOMContentLoaded", async function() {
return [...nodes].sort((a, b) => {
let A = a[key];
let B = b[key];
if (key === "last_seen_us") {
A = A || 0;
B = B || 0;
}
if (A < B) return asc ? -1 : 1;
if (A > B) return asc ? 1 : -1;
return 0;