diff --git a/data/settings.css b/data/settings.css index 51c6621..1b88243 100644 --- a/data/settings.css +++ b/data/settings.css @@ -1,416 +1,420 @@ * { - margin: 0; - padding: 0; - box-sizing: border-box; -} + margin: 0; + padding: 0; + box-sizing: border-box; + } -body { - font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - min-height: 100vh; - padding: 20px; -} + body { + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + padding: 20px; + } -.container { - max-width: 600px; - margin: 0 auto; - background: rgba(255, 255, 255, 0.95); - border-radius: 20px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); - overflow: hidden; - backdrop-filter: blur(10px); -} + .container { + max-width: 600px; + margin: 0 auto; + background: rgba(255, 255, 255, 0.95); + border-radius: 20px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); + overflow: hidden; + backdrop-filter: blur(10px); + } -.header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 30px; - text-align: center; - position: relative; -} + .header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 30px; + text-align: center; + position: relative; + } -.header::before { - content: ""; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: url('data:image/svg+xml,'); - opacity: 0.3; -} + .header::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; + } -.header h1 { - font-size: 2.5em; - margin-bottom: 10px; - position: relative; - z-index: 1; -} + .header h1 { + font-size: 2.5em; + margin-bottom: 10px; + position: relative; + z-index: 1; + } -.header p { - font-size: 1.1em; - opacity: 0.9; - position: relative; - z-index: 1; -} + .header p { + font-size: 1.1em; + opacity: 0.9; + position: relative; + z-index: 1; + } -.content { - padding: 40px; -} + .content { + padding: 40px; + } -.nav-buttons { - display: flex; - gap: 15px; - margin-bottom: 30px; -} + .nav-buttons { + display: flex; + gap: 15px; + margin-bottom: 30px; + } -.nav-button { - flex: 1; - padding: 12px 20px; - background: #f8f9fa; - border: 2px solid #e9ecef; - border-radius: 10px; - color: #495057; - text-decoration: none; - text-align: center; - font-weight: 600; - transition: all 0.3s ease; -} + .nav-button { + flex: 1; + padding: 12px 20px; + background: #f8f9fa; + border: 2px solid #e9ecef; + border-radius: 10px; + color: #495057; + text-decoration: none; + text-align: center; + font-weight: 600; + transition: all 0.3s ease; + } -.nav-button:hover { - background: #667eea; - color: white; - border-color: #667eea; - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3); -} + .nav-button:hover { + background: #667eea; + color: white; + border-color: #667eea; + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3); + } -.section { - margin-bottom: 30px; - background: #f8f9fa; - border-radius: 15px; - padding: 25px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); -} + .section { + margin-bottom: 30px; + background: #f8f9fa; + border-radius: 15px; + padding: 25px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); + } -.section h2 { - color: #495057; - margin-bottom: 20px; - font-size: 1.4em; - display: flex; - align-items: center; - gap: 10px; -} + .section h2 { + color: #495057; + margin-bottom: 20px; + font-size: 1.4em; + display: flex; + align-items: center; + gap: 10px; + } -.section h2::before { - content: ""; - width: 4px; - height: 25px; - background: linear-gradient(135deg, #667eea, #764ba2); - border-radius: 2px; -} + .section h2::before { + content: ""; + width: 4px; + height: 25px; + background: linear-gradient(135deg, #667eea, #764ba2); + border-radius: 2px; + } -.form-group { - margin-bottom: 20px; -} + .form-group { + margin-bottom: 20px; + } -.form-group label { - display: block; - margin-bottom: 8px; - color: #495057; - font-weight: 600; -} + .form-group label { + display: block; + margin-bottom: 8px; + color: #495057; + font-weight: 600; + } -.form-group input { - width: 100%; - padding: 12px 15px; - border: 2px solid #e9ecef; - border-radius: 10px; - font-size: 16px; - transition: all 0.3s ease; -} + .form-group input { + width: 100%; + padding: 12px 15px; + border: 2px solid #e9ecef; + border-radius: 10px; + font-size: 16px; + transition: all 0.3s ease; + } -.form-group input:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} + .form-group input:focus { + outline: none; + border-color: #667eea; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + } -.time-row { - display: flex; - gap: 15px; - align-items: end; -} + .time-row { + display: flex; + gap: 15px; + align-items: end; + } -.time-input { - flex: 1; -} + .time-input { + flex: 1; + } -.current-time { - background: white; - padding: 15px; - border-radius: 10px; - text-align: center; - font-family: monospace; - font-size: 18px; - color: #495057; - border: 2px solid #e9ecef; - margin-bottom: 15px; -} + .current-time { + background: white; + padding: 15px; + border-radius: 10px; + text-align: center; + font-family: monospace; + font-size: 18px; + color: #495057; + border: 2px solid #e9ecef; + margin-bottom: 15px; + } -.button-group { - display: flex; - gap: 15px; - flex-wrap: wrap; -} + .button-group { + display: flex; + gap: 15px; + flex-wrap: wrap; + } -.btn { - padding: 15px 25px; - border: none; - border-radius: 12px; - font-size: 16px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - text-decoration: none; - display: inline-block; - text-align: center; - min-width: 150px; -} + .btn { + padding: 15px 25px; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + text-decoration: none; + display: inline-block; + text-align: center; + min-width: 150px; + } -.btn-primary { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; -} + .btn-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + } -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3); -} + .btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3); + } -.btn-secondary { - background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); - color: white; -} + .btn-secondary { + background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); + color: white; + } -.btn-secondary:hover { - transform: translateY(-2px); - box-shadow: 0 10px 25px rgba(116, 185, 255, 0.3); -} + .btn-secondary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(116, 185, 255, 0.3); + } -.btn-warning { - background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); - color: #d84315; -} + .btn-warning { + background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); + color: #d84315; + } -.btn-warning:hover { - transform: translateY(-2px); - box-shadow: 0 10px 25px rgba(252, 182, 159, 0.3); -} + .btn-warning:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(252, 182, 159, 0.3); + } -.btn-danger { - background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); - color: #c62828; -} + .btn-danger { + background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); + color: #c62828; + } -.btn-danger:hover { - transform: translateY(-2px); - box-shadow: 0 10px 25px rgba(255, 154, 158, 0.3); -} + .btn-danger:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(255, 154, 158, 0.3); + } -.btn-disabled { - background: #e9ecef !important; - color: #6c757d !important; - cursor: not-allowed !important; - transform: none !important; - box-shadow: none !important; -} + .btn-disabled { + background: #e9ecef !important; + color: #6c757d !important; + cursor: not-allowed !important; + transform: none !important; + box-shadow: none !important; + } -.btn-disabled:hover { - transform: none !important; - box-shadow: none !important; -} + .btn-disabled:hover { + transform: none !important; + box-shadow: none !important; + } -.restriction-notice { - background: #fff3cd; - color: #856404; - padding: 15px; - border-radius: 10px; - border: 2px solid #ffeaa7; - margin-bottom: 15px; - font-weight: 600; - text-align: center; -} + /* Toggle Buttons für Modus-Auswahl */ + .mode-toggle { + display: flex; + gap: 0; + border: 2px solid #e9ecef; + border-radius: 12px; + overflow: hidden; + background: white; + } -.status-message { - margin-top: 20px; - padding: 15px; - border-radius: 10px; - font-weight: 600; - text-align: center; - display: none; -} + .mode-button { + flex: 1; + padding: 15px 25px; + border: none; + background: white; + color: #495057; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + } -.status-success { - background: #d4edda; - color: #155724; - border: 2px solid #c3e6cb; -} + .mode-button.active { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + } -.status-error { - background: #f8d7da; - color: #721c24; - border: 2px solid #f5c6cb; -} + .mode-button:not(.active):hover { + background: #f8f9fa; + color: #667eea; + } -.status-info { - background: #cce7ff; - color: #004085; - border: 2px solid #b3d9ff; -} + .mode-button:first-child { + border-right: 1px solid #e9ecef; + } -.learning-mode { - display: none; - text-align: center; - padding: 30px; - background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); - border-radius: 15px; - margin-top: 20px; -} + .restriction-notice { + background: #fff3cd; + color: #856404; + padding: 15px; + border-radius: 10px; + border: 2px solid #ffeaa7; + margin-bottom: 15px; + font-weight: 600; + text-align: center; + } -.learning-mode h3 { - color: #d84315; - font-size: 1.5em; - margin-bottom: 15px; -} + .status-message { + margin-top: 20px; + padding: 15px; + border-radius: 10px; + font-weight: 600; + text-align: center; + display: none; + } -.learning-mode p { - color: #bf360c; - font-size: 1.2em; - margin-bottom: 20px; -} + .status-success { + background: #d4edda; + color: #155724; + border: 2px solid #c3e6cb; + } -.pulse { - animation: pulse 2s infinite; -} + .status-error { + background: #f8d7da; + color: #721c24; + border: 2px solid #f5c6cb; + } -@keyframes pulse { - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } - 100% { - transform: scale(1); - } -} + .status-info { + background: #cce7ff; + color: #004085; + border: 2px solid #b3d9ff; + } -@media (max-width: 600px) { - .container { - margin: 10px; - border-radius: 15px; - } + .learning-mode { + display: none; + text-align: center; + padding: 30px; + background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); + border-radius: 15px; + margin-top: 20px; + } - .content { - padding: 20px; - } + .learning-mode h3 { + color: #d84315; + font-size: 1.5em; + margin-bottom: 15px; + } - .nav-buttons { - flex-direction: column; - } + .learning-mode p { + color: #bf360c; + font-size: 1.2em; + margin-bottom: 20px; + } - .button-group { - flex-direction: column; - } + .pulse { + animation: pulse 2s infinite; + } - .btn { - width: 100%; - } + @keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } + } - .time-row { - flex-direction: column; - gap: 10px; - } -} + .section select { + width: 100%; + padding: 12px 16px; + font-size: 16px; + font-family: inherit; + border: 2px solid #e1e5e9; + border-radius: 8px; + background-color: white; + background-image: url("data:image/svg+xml;charset=US-ASCII,"); + background-repeat: no-repeat; + background-position: right 12px center; + background-size: 12px; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + cursor: pointer; + transition: all 0.3s ease; + } -.section select { - width: 100%; - padding: 12px 16px; - font-size: 16px; - font-family: inherit; - border: 2px solid #e1e5e9; - border-radius: 8px; - background-color: white; - background-image: url("data:image/svg+xml;charset=US-ASCII,"); - background-repeat: no-repeat; - background-position: right 12px center; - background-size: 12px; - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; - cursor: pointer; - transition: all 0.3s ease; -} + .section select:hover { + border-color: #007bff; + box-shadow: 0 2px 8px rgba(0, 123, 255, 0.1); + } -.section select:hover { - border-color: #007bff; - box-shadow: 0 2px 8px rgba(0, 123, 255, 0.1); -} + .section select:focus { + outline: none; + border-color: #007bff; + box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); + } -.section select:focus { - outline: none; - border-color: #007bff; - box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); -} + .section select:disabled { + background-color: #f8f9fa; + color: #6c757d; + cursor: not-allowed; + opacity: 0.6; + border-color: #dee2e6; + } -.section select:disabled { - background-color: #f8f9fa; - color: #6c757d; - cursor: not-allowed; - opacity: 0.6; - border-color: #dee2e6; -} + @media (max-width: 600px) { + .container { + margin: 10px; + border-radius: 15px; + } -.section select:disabled:hover { - border-color: #dee2e6; - box-shadow: none; -} + .content { + padding: 20px; + } -/* Option Styling */ -.section select option { - padding: 8px; - font-size: 16px; - background-color: white; - color: #333; -} + .nav-buttons { + flex-direction: column; + } -.section select option:hover { - background-color: #f8f9fa; -} + .button-group { + flex-direction: column; + } -.section select option:disabled { - color: #6c757d; - background-color: #f8f9fa; -} + .btn { + width: 100%; + } -/* Form Group für bessere Abstände */ -.section .form-group { - margin-bottom: 20px; -} + .time-row { + flex-direction: column; + gap: 10px; + } -.section .form-group label { - display: block; - margin-bottom: 8px; - font-weight: 600; - color: #333; - font-size: 14px; -} + .mode-toggle { + flex-direction: column; + gap: 0; + } -/* Responsive Design für kleinere Bildschirme */ -@media (max-width: 768px) { - .section select { - font-size: 16px; /* Verhindert Zoom auf iOS */ - padding: 14px 16px; - } -} + .mode-button:first-child { + border-right: none; + border-bottom: 1px solid #e9ecef; + } + } \ No newline at end of file diff --git a/data/settings.html b/data/settings.html index 831c7ee..1a7abff 100644 --- a/data/settings.html +++ b/data/settings.html @@ -66,6 +66,30 @@ + +
+

🎯 Modus

+
+
+ +
+ + +
+
+
+ +
+
+
+ +

🔧 Grundeinstellungen

@@ -464,6 +488,43 @@ saveLicence(); }); + + // Mode selection function + // Remove active class from all mode buttons + function selectMode(mode) { + document.querySelectorAll('.mode-button').forEach(button => { + button.classList.remove('active'); + }); + + // Add active class to selected button + document.querySelector(`[data-mode="${mode}"]`).classList.add('active'); + } + + // Mode form handler + document.getElementById('modeForm').addEventListener('submit', function(e) { + e.preventDefault(); + + const activeButton = document.querySelector('.mode-button.active'); + const selectedMode = activeButton ? activeButton.getAttribute('data-mode') : 'individual'; + + fetch('/api/set-mode', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: 'mode=' + encodeURIComponent(selectedMode) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showMessage('Modus erfolgreich gespeichert!', 'success'); + } else { + showMessage('Fehler beim Speichern des Modus', 'error'); + } + }) + .catch(error => showMessage('Verbindungsfehler', 'error')); + }); + // Einstellungen laden function loadSettings() { fetch("/api/get-settings") diff --git a/src/gamemodes.h b/src/gamemodes.h new file mode 100644 index 0000000..c4fca1d --- /dev/null +++ b/src/gamemodes.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#include +#include + +void IndividualMode(); +void CompetitionMode(); + +void IndividualMode(const char *action, int lane, int timestamp = 0) { + Serial.printf("Individual Mode Action: %s on Lane %d at %d\n", action, lane, + timestamp); + // Implement individual mode logic here +} + +void CompetitionMode(const char *action, int lane, int timestamp = 0) { + Serial.printf("Competition Mode Action: %s on Lane %d at %d\n", action, lane, + timestamp); + // Implement competition mode logic here +} \ No newline at end of file diff --git a/src/master.h b/src/master.h index 1dfac6f..c839274 100644 --- a/src/master.h +++ b/src/master.h @@ -56,6 +56,7 @@ unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) bool wifimodeAP = false; // AP-Modus deaktiviert String masterlocation; +int operationalMode = 0; // 0=Individual, 1=Wettkampf // Function Declarations void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len); diff --git a/src/webserverrouter.h b/src/webserverrouter.h index 34b62a8..df9529e 100644 --- a/src/webserverrouter.h +++ b/src/webserverrouter.h @@ -280,6 +280,31 @@ void setupRoutes() { request->send(200, "application/json", "{\"success\":true}"); }); + server.on("/api/set-mode", HTTP_POST, [](AsyncWebServerRequest *request) { + Serial.println("/api/set-mode called"); + + String mode; + if (request->hasParam("mode", true)) { + mode = request->getParam("mode", true)->value(); + } + + if (mode.length() > 0) { + // Speichere den Modus + operationalMode = mode == "individual" ? 1 : 0; + Serial.printf("Operational mode set to: %s\n", + operationalMode == 1 ? "Individual" : "Wettkampf"); + // Rückmeldung + DynamicJsonDocument doc(64); + doc["success"] = true; + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); + } else { + request->send(400, "application/json", + "{\"success\":false,\"error\":\"Modus fehlt\"}"); + } + }); + // Statische Dateien server.serveStatic("/", SPIFFS, "/"); server.begin();