You've already forked TEF6686_ESP32
Update look of logbook to match fmdx style
This commit is contained in:
@@ -5355,177 +5355,128 @@ void toggleiMSEQ() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleRoot() {
|
void handleRoot() {
|
||||||
|
// Open the logbook file
|
||||||
fs::File file = SPIFFS.open("/logbook.csv", "r");
|
fs::File file = SPIFFS.open("/logbook.csv", "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
webserver.send(500, "text/plain", "Failed to open logbook");
|
webserver.send(500, "text/plain", "Failed to open logbook");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build HTML response
|
||||||
String html = "<!DOCTYPE html><html lang=\"en\"><head>";
|
String html = "<!DOCTYPE html><html lang=\"en\"><head>";
|
||||||
html += "<meta charset=\"UTF-8\">";
|
html += "<meta charset=\"UTF-8\">";
|
||||||
html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
|
html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
|
||||||
|
|
||||||
// Add CSS styling
|
|
||||||
html += "<style>";
|
html += "<style>";
|
||||||
html += "body {background-color: #1a1a1a; color: white; font-family: 'Arial', sans-serif; margin: 0; padding: 0;}";
|
html += "body {background-color: rgb(32, 34, 40); color: white; font-family: 'Arial', sans-serif;}";
|
||||||
html += "h1 {text-align: center; color: #ffffff; margin-top: 20px; font-size: 32px;}";
|
html += "h1 {text-align: center; margin-top: 20px; font-size: 32px;}";
|
||||||
html += "img {display: block; margin: 0 auto; max-width: 100%; height: auto; padding-top: 20px; cursor: pointer;}";
|
html += "img {display: block; margin: 0 auto; max-width: 100%; height: auto; padding-top: 20px; cursor: pointer;}";
|
||||||
html += "table {width: 90%; margin: 0 auto; border-collapse: collapse; border-radius: 8px; overflow: hidden;}";
|
html += "table {width: 90%; max-width: 1500px; margin: 0 auto; border-radius: 15px; overflow: auto; padding: 20px; background-color: #2e5049; border: 0; border-collapse: separate; border-spacing: 0;}";
|
||||||
html += "th {background-color: #333; color: white; padding: 12px; text-align: left; font-size: 18px; cursor: pointer; position: relative;}";
|
html += "thead th {font-size: 18px; cursor: pointer; position: relative; user-select: none; background-color: #3c7f6a;}";
|
||||||
html += "td {background-color: #2a2a2a; color: white; padding: 10px; text-align: left; font-size: 16px;}";
|
html += "th, td {padding: 10px; text-align: center;}";
|
||||||
html += "tr:nth-child(even) {background-color: #252525;}";
|
html += "thead th:first-child, tbody th:first-child {border-radius: 15px 0 0 15px;}";
|
||||||
html += "tr:hover {background-color: #ff6600 !important;}";
|
html += "thead th:nth-last-of-type(1), tbody th:nth-last-of-type(1) {border-radius: 0 15px 15px 0;}";
|
||||||
html += "tr:hover td {background-color: #ff6600 !important;}";
|
html += "tbody td:nth-child(3) {font-weight: 700;}";
|
||||||
html += "button {background-color: #ffcc00; color: black; border: none; padding: 12px 20px; font-size: 18px; cursor: pointer; border-radius: 5px; display: block; margin: 20px auto;}";
|
html += "tbody td:nth-child(5) {color: #ddd;}";
|
||||||
html += "button:hover {background-color: #ff9900;}";
|
html += "tbody td:nth-child(6) {color: #5bd6ab; font-weight: bold;}";
|
||||||
html += "@media (max-width: 768px) { table {width: 100%;} th, td {font-size: 14px; padding: 8px;} }";
|
html += "thead th:nth-child(2), thead th:nth-child(8) {width: 5%;} thead th:nth-child(1), thead th:nth-child(4) {width: 10%;} thead th:nth-child(3) {width: 15%;}";
|
||||||
html += ".go-to-bottom {position: fixed; bottom: 30px; right: 30px; background-color: #ffcc00; color: black; border: none; padding: 12px 20px; font-size: 18px; cursor: pointer; border-radius: 5px; z-index: 100;}";
|
html += "button {background-color: #4db691; font-family: 'Arial', sans-serif; border: 0; padding: 15px 20px; font-size: 14px; text-transform: uppercase; cursor: pointer; border-radius: 15px; font-weight: bold; color: rgb(32, 34, 40); display: block; margin: 20px auto; transition: 0.3s ease background-color;}";
|
||||||
html += ".sort-icon {font-size: 18px; position: absolute; right: 10px; top: 50%; transform: translateY(-50%);}";
|
html += "button:hover {background-color: #5bd6ab;}";
|
||||||
html += "</style>";
|
html += "a { text-decoration:none };";
|
||||||
|
html += ".go-to-bottom {position: fixed; bottom: 30px; right: 30px; z-index: 100;}";
|
||||||
html += "</head><body>";
|
html += ".sort-icon {position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: #ccc;}";
|
||||||
|
html += "@media (max-width: 768px) {table {width: 100%;} th, td {font-size: 14px; padding: 8px;}}";
|
||||||
// Add logo as a clickable link
|
html += "</style></head><body>";
|
||||||
html += "<a href=\"https://fmdx.org/\" target=\"_blank\">";
|
html += "<a href=\"https://fmdx.org/\" target=\"_blank\">";
|
||||||
html += "<img src=\"/logo.png\" alt=\"FMDX logo\">";
|
html += "<img src=\"/logo.png\" alt=\"FMDX website\">";
|
||||||
html += "</a>";
|
html += "</a>";
|
||||||
|
|
||||||
html += "<h1>" + String(myLanguage[language][286]) + "</h1>";
|
html += "<h1>" + String(myLanguage[language][286]) + "</h1>";
|
||||||
html += "<button onclick=\"window.location.href='/downloadCSV'\">" + String(myLanguage[language][287]) + "</button>";
|
html += "<button onclick=\"window.location.href='/downloadCSV'\">" + String(myLanguage[language][287]) + "</button>";
|
||||||
html += "<button class=\"go-to-bottom\" onclick=\"scrollToBottom()\">" + String(myLanguage[language][289]) + "</button>";
|
html += "<button class=\"go-to-bottom\" onclick=\"window.scrollTo(0, document.body.scrollHeight);\">" + String(myLanguage[language][289]) + "</button>";
|
||||||
|
|
||||||
// JavaScript for scrolling and sorting
|
|
||||||
html += "<script>";
|
html += "<script>";
|
||||||
html += "function scrollToBottom() { window.scrollTo(0, document.body.scrollHeight); }";
|
|
||||||
|
|
||||||
// Sorting function with icons
|
|
||||||
html += "function sortTable(columnIndex) {";
|
html += "function sortTable(columnIndex) {";
|
||||||
html += " var table = document.getElementById('logbookTable');";
|
html += "var table = document.getElementById('logbookTable');";
|
||||||
html += " var rows = Array.from(table.rows).slice(1);"; // Skip header row
|
html += "var rows = Array.from(table.rows).slice(1);";
|
||||||
html += " var isAscending = table.rows[0].cells[columnIndex].classList.toggle('asc');";
|
html += "var isAscending = table.rows[0].cells[columnIndex].classList.toggle('asc');";
|
||||||
html += " var headerCells = table.rows[0].cells;";
|
html += "Array.from(table.rows[0].cells).forEach((th, index) => { if (index !== columnIndex) th.classList.remove('asc', 'desc'); });";
|
||||||
html += " for (var i = 0; i < headerCells.length; i++) {";
|
html += "rows.sort((a, b) => {";
|
||||||
html += " if (i !== columnIndex) { headerCells[i].classList.remove('asc', 'desc'); }";
|
html += "var cellA = a.cells[columnIndex].textContent.trim();";
|
||||||
html += " }";
|
html += "var cellB = b.cells[columnIndex].textContent.trim();";
|
||||||
html += " rows.sort(function(a, b) {";
|
html += "return isAscending ? cellA.localeCompare(cellB) : cellB.localeCompare(cellA);});";
|
||||||
html += " var cellA = a.cells[columnIndex].textContent.trim();";
|
html += "rows.forEach(row => table.appendChild(row));";
|
||||||
html += " var cellB = b.cells[columnIndex].textContent.trim();";
|
html += "updateSortIcons(columnIndex, isAscending);}";
|
||||||
html += " if (isAscending) {";
|
|
||||||
html += " return cellA > cellB ? 1 : (cellA < cellB ? -1 : 0);";
|
|
||||||
html += " } else {";
|
|
||||||
html += " return cellA < cellB ? 1 : (cellA > cellB ? -1 : 0);";
|
|
||||||
html += " }";
|
|
||||||
html += " });";
|
|
||||||
html += " rows.forEach(function(row) { table.appendChild(row); });"; // Reorder rows";
|
|
||||||
html += " updateSortIcons(columnIndex, isAscending);"; // Update sort icons after sorting
|
|
||||||
html += "}";
|
|
||||||
|
|
||||||
// Function to update the sort icons
|
|
||||||
html += "function updateSortIcons(columnIndex, isAscending) {";
|
html += "function updateSortIcons(columnIndex, isAscending) {";
|
||||||
html += " var headers = document.querySelectorAll('th');";
|
html += "document.querySelectorAll('th').forEach((th, index) => {";
|
||||||
html += " for (var i = 0; i < headers.length; i++) {";
|
html += "th.querySelector('.sort-icon').textContent = '';";
|
||||||
html += " var icon = headers[i].querySelector('.sort-icon');";
|
html += "if (index === columnIndex) { th.querySelector('.sort-icon').textContent = isAscending ? '▲' : '▼';}});}";
|
||||||
html += " if (icon) { icon.remove(); }"; // Remove old icon
|
|
||||||
html += " if (i === columnIndex) {";
|
|
||||||
html += " var newIcon = document.createElement('span');";
|
|
||||||
html += " newIcon.className = 'sort-icon';";
|
|
||||||
html += " newIcon.textContent = isAscending ? '↑' : '↓';";
|
|
||||||
html += " headers[i].appendChild(newIcon);";
|
|
||||||
html += " }";
|
|
||||||
html += " }";
|
|
||||||
html += "}";
|
|
||||||
|
|
||||||
html += "</script>";
|
html += "</script>";
|
||||||
|
|
||||||
html += "<table id=\"logbookTable\">";
|
// Generate table header and body
|
||||||
|
html += "<table id=\"logbookTable\"><thead><tr>";
|
||||||
String header = "";
|
String header = file.available() ? file.readStringUntil('\n') : "";
|
||||||
if (file.available()) {
|
if (header.length() > 0) {
|
||||||
header = file.readStringUntil('\n');
|
|
||||||
html += "<tr>";
|
|
||||||
int startIndex = 0;
|
int startIndex = 0;
|
||||||
int columnIndex = 0;
|
int columnIndex = 0;
|
||||||
|
|
||||||
// Generate table headers with sorting functionality
|
|
||||||
while (startIndex < header.length()) {
|
while (startIndex < header.length()) {
|
||||||
int endIndex = header.indexOf(',', startIndex);
|
int endIndex = header.indexOf(',', startIndex);
|
||||||
if (endIndex == -1) endIndex = header.length();
|
if (endIndex == -1) endIndex = header.length();
|
||||||
String column = header.substring(startIndex, endIndex);
|
String column = header.substring(startIndex, endIndex);
|
||||||
|
|
||||||
// Add clickable headers for sorting
|
|
||||||
html += "<th onclick=\"sortTable(" + String(columnIndex) + ")\">" + column;
|
html += "<th onclick=\"sortTable(" + String(columnIndex) + ")\">" + column;
|
||||||
html += "<span class=\"sort-icon\"></span></th>"; // Sorting icon placeholder
|
html += "<span class=\"sort-icon\"></span></th>";
|
||||||
|
|
||||||
startIndex = endIndex + 1;
|
startIndex = endIndex + 1;
|
||||||
columnIndex++;
|
columnIndex++;
|
||||||
}
|
}
|
||||||
html += "</tr>";
|
html += "<th></th></tr></thead><tbody>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate table rows
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
int piCodeIndex = -1, frequencyIndex = -1;
|
int piCodeIndex = -1;
|
||||||
|
int frequencyIndex = -1;
|
||||||
// Identify column indices for PI code and Frequency
|
int startIndex = 0;
|
||||||
int startIndex = 0, columnIndex = 0;
|
int columnIndex = 0;
|
||||||
while (startIndex < header.length()) {
|
while (startIndex < header.length()) {
|
||||||
int endIndex = header.indexOf(',', startIndex);
|
int endIndex = header.indexOf(',', startIndex);
|
||||||
if (endIndex == -1) endIndex = header.length();
|
if (endIndex == -1) endIndex = header.length();
|
||||||
String column = header.substring(startIndex, endIndex);
|
String column = header.substring(startIndex, endIndex);
|
||||||
|
|
||||||
if (column.equalsIgnoreCase("PI code")) piCodeIndex = columnIndex;
|
if (column.equalsIgnoreCase("PI code")) piCodeIndex = columnIndex;
|
||||||
if (column.equalsIgnoreCase("Frequency")) frequencyIndex = columnIndex;
|
if (column.equalsIgnoreCase("Frequency")) frequencyIndex = columnIndex;
|
||||||
|
|
||||||
startIndex = endIndex + 1;
|
startIndex = endIndex + 1;
|
||||||
columnIndex++;
|
columnIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate rows
|
|
||||||
while (file.available()) {
|
while (file.available()) {
|
||||||
String line = file.readStringUntil('\n');
|
String line = file.readStringUntil('\n');
|
||||||
if (line.length() > 0) {
|
if (line.length() > 0) {
|
||||||
hasData = true;
|
hasData = true;
|
||||||
html += "<tr>";
|
html += "<tr>";
|
||||||
|
String piCode = "";
|
||||||
String piCode = "", frequency = "";
|
String frequency = "";
|
||||||
startIndex = 0, columnIndex = 0;
|
startIndex = 0;
|
||||||
|
columnIndex = 0;
|
||||||
while (startIndex < line.length()) {
|
while (startIndex < line.length()) {
|
||||||
int endIndex = line.indexOf(',', startIndex);
|
int endIndex = line.indexOf(',', startIndex);
|
||||||
if (endIndex == -1) endIndex = line.length();
|
if (endIndex == -1) endIndex = line.length();
|
||||||
String cell = line.substring(startIndex, endIndex);
|
String cell = line.substring(startIndex, endIndex);
|
||||||
|
|
||||||
// Extract PI code and Frequency
|
|
||||||
if (columnIndex == piCodeIndex) piCode = cell;
|
if (columnIndex == piCodeIndex) piCode = cell;
|
||||||
if (columnIndex == frequencyIndex) frequency = cell;
|
if (columnIndex == frequencyIndex) frequency = cell;
|
||||||
|
|
||||||
html += "<td>" + cell + "</td>";
|
html += "<td>" + cell + "</td>";
|
||||||
startIndex = endIndex + 1;
|
startIndex = endIndex + 1;
|
||||||
columnIndex++;
|
columnIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove " MHz" from Frequency
|
|
||||||
frequency.replace(" MHz", "");
|
frequency.replace(" MHz", "");
|
||||||
|
html += "<td><a href=\"https://maps.fmdx.org/#qth=&freq=" + frequency + "&findPi=" + piCode + "\" target=\"_blank\">🌐</a></td>";
|
||||||
// Make row clickable
|
|
||||||
html += "<script>";
|
|
||||||
html += "document.querySelector('tr:last-child').onclick = function() {";
|
|
||||||
html += " window.open('https://maps.fmdx.org/#qth=&freq=" + frequency + "&findPi=" + piCode + "', '_blank');";
|
|
||||||
html += "};";
|
|
||||||
html += "</script>";
|
|
||||||
|
|
||||||
html += "</tr>";
|
html += "</tr>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle empty table case
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
if (!hasData) {
|
if (!hasData) {
|
||||||
html += "<tr><td colspan=\"100%\" style=\"text-align: center; color: red;\">" + String(myLanguage[language][288]) + "</td></tr>";
|
html += "<tr><td colspan=\"100%\" style=\"text-align: center; color: red;\">" + String(myLanguage[language][288]) + "</td></tr>";
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</table>";
|
html += "</tbody></table>";
|
||||||
html += "</body></html>";
|
html += "</body></html>";
|
||||||
|
|
||||||
webserver.send(200, "text/html", html);
|
webserver.send(200, "text/html", html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user