diff --git a/WEBROOT/api/js/handler/NotificationAjaxHandler.php b/WEBROOT/api/js/handler/NotificationAjaxHandler.php new file mode 100644 index 0000000..012af86 --- /dev/null +++ b/WEBROOT/api/js/handler/NotificationAjaxHandler.php @@ -0,0 +1,55 @@ + 0, + 'path' => '/', + 'domain' => '', + 'secure' => false, + 'httponly' => true, + 'samesite' => 'Lax' +]); +session_start(); + +header('Content-Type: application/json'); +error_reporting(E_ALL); +ini_set('display_errors', '0'); + +if (empty($_SESSION['user_logged_in'])) { + http_response_code(401); + echo json_encode(['error' => 'Nicht eingeloggt']); + exit; +} + +include_once(__DIR__ . '/../../../controller/DatabaseController.php'); +include_once(__DIR__ . '/../../../controller/NotifyController.php'); + +$daten = json_decode(file_get_contents('php://input'), true) ?? []; +$action = $daten['action'] ?? ''; + +try { + switch ($action) { + + case 'mark_seen': { + $nachrichtId = $daten['nachricht_id'] ?? null; + + if (!is_numeric($nachrichtId)) { + throw new InvalidArgumentException('Ungültige Nachrichten-ID'); + } + + $notificationCtl = new Notification(); + $notificationCtl->markAsSeen((int)$nachrichtId); + + echo json_encode(['success' => true]); + break; + } + + default: + throw new InvalidArgumentException("Unbekannte Aktion: $action"); + } +} catch (InvalidArgumentException $e) { + http_response_code(400); + echo json_encode(['error' => $e->getMessage()]); +} catch (Throwable $e) { + http_response_code(500); + error_log($e->getMessage()); + echo json_encode(['error' => 'Serverfehler']); +} diff --git a/WEBROOT/controller/NotifyController.php b/WEBROOT/controller/NotifyController.php new file mode 100644 index 0000000..7a10c33 --- /dev/null +++ b/WEBROOT/controller/NotifyController.php @@ -0,0 +1,132 @@ +table = "Benachrichtigung"; + $this->seenTable = "Benachrichtigung_Gesehen"; + } + + /** + * Ermittelt die Benutzer-ID des aktuell eingeloggten Nutzers. + */ + private function getCurrentUserId(): ?int + { + $username = $_SESSION['username'] ?? null; + if (!$username) { + return null; + } + + $rows = DatabaseShow( + "Benutzer", + "ID", + "NAME = ?", + $username + ); + + return $rows[0]['ID'] ?? null; + } + + /** + * Alle Nachrichten (z.B. für eine Verwaltungsansicht). + */ + public function getAllMessages(): array + { + return DatabaseShow( + $this->table, + "ID, TITEL, NACHRICHT" + ); + } + + /** + * Nur die Nachrichten, die der aktuelle Nutzer noch NICHT gesehen hat. + * Genau das braucht das Modal, um zu entscheiden ob/was es anzeigt. + */ + public function getUnseenMessages(): array + { + $userId = $this->getCurrentUserId(); + if ($userId === null) { + return []; + } + + $alleNachrichten = DatabaseShow( + $this->table, + "ID, TITEL, NACHRICHT" + ); + + $gesehenRows = DatabaseShow( + $this->seenTable, + "NACHRICHTEN_ID", + "BENUTZER_ID = ?", + $userId + ); + + $gesehenIds = array_column($gesehenRows, 'NACHRICHTEN_ID'); + + return array_values(array_filter( + $alleNachrichten, + fn($nachricht) => !in_array($nachricht['ID'], $gesehenIds) + )); + } + + /** + * Legt eine neue Nachricht im Pool an (z.B. für eine Admin-Funktion). + */ + public function createMessage(string $titel, string $nachricht): bool + { + try { + DatabaseInsert( + $this->table, + "TITEL, NACHRICHT", + $titel, + $nachricht + ); + return true; + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + } + + /** + * Markiert eine bestimmte Nachricht als vom aktuellen Nutzer gesehen/bestätigt. + */ + public function markAsSeen(int $nachrichtId): bool + { + $userId = $this->getCurrentUserId(); + if ($userId === null) { + throw new Exception('Kein eingeloggter Benutzer gefunden.'); + } + + // Prüfen, ob schon als gesehen markiert, um Duplikate zu vermeiden + $bereitsGesehen = DatabaseShow( + $this->seenTable, + "ID", + "BENUTZER_ID = ? AND NACHRICHTEN_ID = ?", + $userId, + $nachrichtId + ); + + if (!empty($bereitsGesehen)) { + return true; // war schon markiert, kein Fehler + } + + try { + DatabaseInsert( + $this->seenTable, + "BENUTZER_ID, NACHRICHTEN_ID", + $userId, + $nachrichtId + ); + return true; + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + } +} diff --git a/WEBROOT/js/ajaxApi/NotificationAjaxMethods.js b/WEBROOT/js/ajaxApi/NotificationAjaxMethods.js new file mode 100644 index 0000000..477f958 --- /dev/null +++ b/WEBROOT/js/ajaxApi/NotificationAjaxMethods.js @@ -0,0 +1,25 @@ +const NOTIFICATION_HANDLER_URL = '/api/js/handler/NotificationAjaxHandler.php'; + +async function notificationAjaxRequest(payload) { + try { + const response = await fetch(NOTIFICATION_HANDLER_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + const data = await parseJsonResponse(response); + + if (data.error) { + throw new Error(data.error); + } + return data; + + } catch (error) { + console.error('Notification-Ajax-Fehler:', error.message); + throw error; + } +} + +function markNotificationAsSeen(nachrichtId) { + return notificationAjaxRequest({ action: 'mark_seen', nachricht_id: nachrichtId }); +} \ No newline at end of file diff --git a/WEBROOT/services/home_info.php b/WEBROOT/services/home_info.php index 39dc69b..69c9da4 100644 --- a/WEBROOT/services/home_info.php +++ b/WEBROOT/services/home_info.php @@ -1,4 +1,6 @@ getUnseenMessages(); +$aktuelleNachricht = $offeneNachrichten[0] ?? null; // immer nur die erste anzeigen + ?> + +
+
+
📢
+

+

+ +
+
+ +
@@ -41,18 +59,18 @@ $db_res = DatabaseShow(
@@ -67,21 +85,21 @@ $db_res = DatabaseShow( Aktiv

-

Client IP: - getClientIP()) ?> -

-

Server IP: - getServerIP()) ?> -

-

Geo IP: - getGeoIP((new getSpecificData())->getClientIP())) ?> -

-

Database IP: - getDatabase()) ?> -

-

Wetter: - getWeather()['aktuell']) ?> -

+

Client IP: + getClientIP()) ?> +

+

Server IP: + getServerIP()) ?> +

+

Geo IP: + getGeoIP((new getSpecificData())->getClientIP())) ?> +

+

Database IP: + getDatabase()) ?> +

+

Wetter: + getWeather()['aktuell']) ?> +

@@ -89,32 +107,55 @@ $db_res = DatabaseShow( + + + + \ No newline at end of file diff --git a/WEBROOT/style.css b/WEBROOT/style.css index c611d0e..0d7b4f7 100644 --- a/WEBROOT/style.css +++ b/WEBROOT/style.css @@ -1460,4 +1460,88 @@ body.dark-mode .autocomplete-suggestions div.active { .register-form input { margin-bottom: 0; /* gap übernimmt jetzt den Abstand, kein doppeltes Spacing mehr */ +} + +/* ===== Benachrichtigungs-Modal ===== */ +.notification-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; + + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease; +} + +.notification-overlay.open { + opacity: 1; + pointer-events: auto; +} + +.notification-box { + background-color: white; + border-radius: 10px; + border-top: 4px solid #007bff; + width: 420px; + max-width: 90%; + padding: 28px 32px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); + text-align: center; + + transform: translateY(-10px); + transition: transform 0.2s ease; +} + +.notification-overlay.open .notification-box { + transform: translateY(0); +} + +body.dark-mode .notification-box { + background-color: #1a1a1a; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); +} + +.notification-box .notification-icon { + font-size: 2.2rem; + margin-bottom: 12px; +} + +.notification-box h3 { + margin-top: 0; + margin-bottom: 12px; + font-size: 1.3rem; +} + +body.dark-mode .notification-box h3 { + color: #f0f0f0; +} + +.notification-box p { + margin-bottom: 24px; + line-height: 1.5; + color: #555; + white-space: pre-line; +} + +body.dark-mode .notification-box p { + color: #ccc; +} + +.notification-box button { + padding: 10px 28px; + border: none; + border-radius: 4px; + font-size: 1rem; + cursor: pointer; + background-color: #007bff; + color: white; + transition: background-color 0.2s ease; +} + +.notification-box button:hover { + background-color: #0056b3; } \ No newline at end of file