add settings locations
Some checks failed
/ build (push) Has been cancelled

This commit is contained in:
Carsten Graf
2025-09-08 22:30:15 +02:00
parent 55eb062d2c
commit 173b13fcfc
13 changed files with 940 additions and 247 deletions

View File

@@ -11,6 +11,22 @@
<title>Ninjacross Timer - Einstellungen</title>
</head>
<body>
<!-- Modern Notification Toast -->
<div id="notificationBubble" class="notification-toast" style="display: none;">
<div class="notification-icon">
<span id="notificationIcon"></span>
</div>
<div class="notification-body">
<div class="notification-title" id="notificationTitle">Erfolg</div>
<div class="notification-message" id="notificationText">Bereit</div>
</div>
<button class="notification-close" onclick="hideNotification()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z"/>
</svg>
</button>
</div>
<div class="container">
<!-- Header Section -->
<div class="header">
@@ -25,9 +41,6 @@
<a href="/rfid" class="nav-button">📡 RFID</a>
</div>
<!-- Status Message Container -->
<div id="statusMessage" class="status-message"></div>
<!-- Date & Time Section -->
<div class="section">
<h2>🕐 Datum & Uhrzeit</h2>
@@ -595,7 +608,7 @@
.then((response) => response.json())
.then((data) => {
document.getElementById("licencekey").value = data.licence || "";
loadLocations();
loadLocationsFromBackend();
})
.catch((error) =>
showMessage("Fehler beim Laden der Lizenz", "error")
@@ -1004,44 +1017,84 @@
//location functions
// Locations laden und Dropdown befüllen
function loadLocations() {
const licence = document.getElementById("licencekey").value; // Get the licence key from the input field
fetch("http://db.reptilfpv.de:3000/api/location/", {
method: "GET",
headers: {
Authorization: `Bearer ${licence}`, // Add Bearer token using licenkey
},
})
.then((response) => response.json())
.then((data) => {
const select = document.getElementById("locationSelect");
function loadLocationsFromBackend() {
const select = document.getElementById("locationSelect");
// Vorhandene Optionen löschen (außer der ersten "Bitte wählen...")
while (select.children.length > 1) {
select.removeChild(select.lastChild);
}
// Vorhandene Optionen löschen (außer der ersten "Bitte wählen...")
while (select.children.length > 1) {
select.removeChild(select.lastChild);
}
// Neue Optionen aus Backend-Response hinzufügen
// Neue Optionen aus Backend-Response hinzufügen
data.forEach((location) => {
const option = document.createElement("option");
option.value = location.id;
option.textContent = location.name;
select.appendChild(option);
});
// Aktuell gespeicherten Standort laden
loadCurrentLocation();
// Fallback: Statische Standorte falls API nicht verfügbar ist
const staticLocations = [
{ id: "1", name: "Hauptstandort" },
{ id: "2", name: "Standort A" },
{ id: "3", name: "Standort B" },
{ id: "4", name: "Teststandort" }
];
// Versuche zuerst die echte API zu verwenden
const licence = document.getElementById("licencekey").value;
if (licence && licence.trim() !== "") {
fetch("https://ninja.reptilfpv.de/api/v1/private/locations", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${licence}`,
},
})
.catch((error) => {
console.log("Locations konnten nicht geladen werden:", error);
showMessage("Fehler beim Laden der Locations", "error");
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then((data) => {
if (data.success && data.data) {
// API erfolgreich - verwende echte Daten
data.data.forEach((location) => {
const option = document.createElement("option");
option.value = location.id;
option.textContent = location.name;
select.appendChild(option);
});
showMessage("Standorte erfolgreich von API geladen", "success");
} else {
throw new Error("Ungültige API-Response");
}
// Aktuell gespeicherten Standort laden
loadSavedLocation();
})
.catch((error) => {
console.log("API nicht verfügbar, verwende statische Daten:", error);
// API fehlgeschlagen - verwende statische Daten als Fallback
staticLocations.forEach((location) => {
const option = document.createElement("option");
option.value = location.id;
option.textContent = location.name;
select.appendChild(option);
});
showMessage("Standorte geladen (statische Daten - API nicht verfügbar)", "warning");
// Aktuell gespeicherten Standort laden
loadSavedLocation();
});
} else {
// Kein Lizenz-Key - verwende statische Daten
staticLocations.forEach((location) => {
const option = document.createElement("option");
option.value = location.id;
option.textContent = location.name;
select.appendChild(option);
});
showMessage("Standorte geladen (statische Daten - kein Lizenz-Key)", "warning");
// Aktuell gespeicherten Standort laden
loadSavedLocation();
}
}
// Aktuell gespeicherten Standort laden
function loadCurrentLocation() {
fetch("/api/get-location")
function loadSavedLocation() {
fetch("/api/get-local-location")
.then((response) => response.json())
.then((data) => {
if (data.locationId) {
@@ -1114,7 +1167,7 @@
}
// Standort an Backend senden
fetch("/api/set-location", {
fetch("/api/set-local-location", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
@@ -1132,18 +1185,90 @@
.catch((error) => showMessage("Verbindungsfehler", "error"));
});
// Status-Nachricht anzeigen
function showMessage(message, type) {
const statusDiv = document.getElementById("statusMessage");
statusDiv.textContent = message;
statusDiv.className = `status-message status-${type}`;
statusDiv.style.display = "block";
// Moderne Notification anzeigen
function showMessage(message, type = 'info') {
console.log("showMessage called:", message, type);
const toast = document.getElementById("notificationBubble");
const icon = document.getElementById("notificationIcon");
const title = document.getElementById("notificationTitle");
const text = document.getElementById("notificationText");
if (!toast || !icon || !title || !text) {
console.error("Notification elements not found!");
return;
}
// Clear any existing timeout
if (window.notificationTimeout) {
clearTimeout(window.notificationTimeout);
}
// Set content
text.textContent = message;
// Set type-specific styling and content
toast.className = "notification-toast";
switch(type) {
case 'success':
toast.classList.add('success');
icon.textContent = '✓';
title.textContent = 'Erfolg';
break;
case 'error':
toast.classList.add('error');
icon.textContent = '✕';
title.textContent = 'Fehler';
break;
case 'info':
toast.classList.add('info');
icon.textContent = '';
title.textContent = 'Information';
break;
case 'warning':
toast.classList.add('warning');
icon.textContent = '⚠';
title.textContent = 'Warnung';
break;
default:
toast.classList.add('info');
icon.textContent = '';
title.textContent = 'Information';
}
// Show toast with animation
toast.style.display = "flex";
// Force reflow
toast.offsetHeight;
// Add show class after a small delay to ensure display is set
setTimeout(() => {
statusDiv.style.display = "none";
toast.classList.add('show');
}, 10);
// Auto-hide after 5 seconds
window.notificationTimeout = setTimeout(() => {
hideNotification();
}, 5000);
}
// Notification verstecken mit Animation
function hideNotification() {
const toast = document.getElementById("notificationBubble");
if (!toast) return;
// Clear timeout if exists
if (window.notificationTimeout) {
clearTimeout(window.notificationTimeout);
}
// Remove show class for animation
toast.classList.remove('show');
// Hide after animation completes
setTimeout(() => {
toast.style.display = "none";
}, 400); // Match CSS transition duration
}
// System-Info alle 30 Sekunden aktualisieren
setInterval(loadSystemInfo, 30000);
</script>