This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user