mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-03-04 23:27:46 +01:00
minor fix on node.html table of tackets shows to and from not just from.
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user