From 092d5552fc97c401a96cbe8a038d1d3cc52b8f0f Mon Sep 17 00:00:00 2001
From: Tim Kreuzer <t.kreuzer@fz-juelich.de>
Date: Tue, 11 Feb 2025 17:20:36 +0100
Subject: [PATCH] update jupyter4nfdi frontend to new frontend version

---
 templates/footer.html | 86 ++++++++++++++++++++++++++++++++++++-------
 templates/header.html | 15 +++++---
 templates/page.html   | 37 +++++++++++++++++--
 3 files changed, 114 insertions(+), 24 deletions(-)

diff --git a/templates/footer.html b/templates/footer.html
index 26fc5e2..0e26b10 100644
--- a/templates/footer.html
+++ b/templates/footer.html
@@ -313,22 +313,80 @@ require(["jquery", "home/utils"], function (
   $(window).resize(function () {
     createCarouselPages();
   });
-  
+
   $(document).ready(function() {
     createCarouselPages();
-    updateSystemHoverTooltips();
-    utils.updateNumberOfUsers();
-  })
-
-  if (!(window.location.pathname.endsWith("home") || window.location.pathname.includes("spawn-pending"))) {
-    console.log("setup SSE")
-    let userSpawnerNotificationUrl = `${jhdata.base_url}api/users/${jhdata.user}/notifications/spawners?_xsrf=${window.jhdata.xsrf_token}`;
-    evtSourcesGlobal["footer"] = new EventSource(userSpawnerNotificationUrl);
-    evtSourcesGlobal["footer"].onmessage = (e) => {
-      utils.updateNumberOfUsers();
-    };
-  }
-  
+  });
+
+  const incidentsThresholdInteractiveFooter = {{ custom_config.get("incidentCheck", {}).get("healthThreshold", {}).get("interactive", 50) | tojson }};
+
+  $(`[data-sse-incidents]`).on("sse", function (event, incidents) {
+    $(`[id^='ampel-'][id$='-tooltip']`).tooltip("dispose");
+    let newMaintenanceList = [];
+    for (const [system, systemInfo] of Object.entries(incidents)) {
+      if (systemInfo.incident) {
+        $(`#ampel-${system.toLowerCase()}-tooltip`)
+          .attr('data-bs-original-title', systemInfo.incident);
+        if ( systemInfo.health > incidentsThresholdInteractiveFooter ) {
+          const _system = incidentsmapping[system] ?? system;
+          if ( !newMaintenanceList.includes(_system) ) {
+            newMaintenanceList.push(_system);
+          }
+        }
+      }
+    }
+    globalMaintenanceSystems = newMaintenanceList;
+    $(`[id$='-option-input']`).trigger("option");
+    $(`[id^='ampel-'][id$='-tooltip']`).tooltip();
+    reorderSystems(incidents);
+  });  
+
+  $(`[data-sse-usercount]`).on("sse", function (event, data) {
+    var systems = {};
+    $("div[id^='ampel']").each((i, e) => {
+      let system = $(e).attr("id").split('-')[1];
+      systems[system] = false;
+    })
+    $(`[id^='ampel-'][id$='-tooltip']`).tooltip("dispose");
+    for (const [system, usercount] of Object.entries(data)) {
+      switch (system) {
+        case 'jupyterhub':
+          $("#jupyter-users").html(usercount);
+          systems['jupyter'] = true;
+          break;
+        case 'JSC-Cloud':
+          $(`#jsccloud-users`).html(usercount['total']);
+          systems['jsccloud'] = true;
+          break;
+        default:
+          $(`#${system.toLowerCase()}-users`).html(usercount['total']);
+          systems[`${system.toLowerCase()}`] = true;
+          var partitionInfos = "";
+          for (const [partition, users] of Object.entries(usercount['partitions'])) {
+            partitionInfos += `\n${partition}: ${users}`;
+          }
+          $(`#${system.toLowerCase()}-users`)
+            .parents("[data-toggle]").tooltip("dispose");
+          $(`#${system.toLowerCase()}-users`)
+            .parents("[data-bs-toggle]")
+            .attr("data-bs-original-title", `Number of active servers${partitionInfos}`);
+          $(`#${system.toLowerCase()}-users`)
+            .parents("[data-toggle]").tooltip();
+      }
+    }
+    // If there was no info about a system, set running labs to 0 and reset tooltip
+    for (const [system, systemInfo] of Object.entries(systems)) {
+      if (systemInfo == false) {
+        $(`#${system.toLowerCase()}-users`)
+          .parents("[data-toggle]").tooltip("dispose");
+        $(`#${system.toLowerCase()}-users`).html(0);
+        $(`#${system.toLowerCase()}-users`)
+          .parents("[data-toggle]")
+          .attr("data-bs-original-title", `Number of active servers`);
+        $(`#${system.toLowerCase()}-users`)
+      }
+    }
+  });
 })
 </script>
 {%- endblock -%}
diff --git a/templates/header.html b/templates/header.html
index d1ae738..1b857f0 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -1,13 +1,13 @@
 {%- macro create_user_widget() -%}
 {%- if user %}
 <div class="dropdown">
-  <button class="btn btn-outline-primary dropdown-toggle mb-2" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside">{{ user.name }}</button>
+  {%- if auth_state %}
+    {%- set display_name = auth_state.get("oauth_user", {}).get("name", user.name) %}
+  {%- else %}
+    {%- set display_name = user.name %}
+  {%- endif %}
+  <button class="btn btn-outline-primary dropdown-toggle mb-2" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside">{{ display_name }}</button>
   <ul class="dropdown-menu dropdown-menu-end w-100">
-    {%- if auth_state and auth_state.get("last_login", False) %}
-    <li><h6 class="dropdown-header">Last login</h6></li>
-    <li><a class="dropdown-item disabled text-black">{{ auth_state.get("last_login") }}</a></li>
-    <li><hr class="dropdown-divider"></li>
-    {%- endif -%}
     <li>
       <button id="logout" class="dropdown-item">
         <svg class="align-middle" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-right" viewBox="0 0 16 16">
@@ -32,6 +32,9 @@
   <div class="d-flex">
     {%- if user %}
     <li class="nav-item"><a id="{{prefix}}start-nav-item" class="nav-link text-decoration-none" href="{{ base_url }}home">JupyterLab</a></li>
+    {%- if auth_state and "geant:dfn.de:fz-juelich.de:jsc:jupyter:workshop_instructors" in auth_state.get("groups", []) %}
+    <li class="nav-item"><a id="{{prefix}}workshop-manage-nav-item" class="nav-link text-decoration-none" href="{{ base_url }}workshopmanager">Manage Workshops</a></li>
+    {%- endif -%}
     {%- if user.admin %}
     <li class="nav-item"><a id="{{prefix}}admin-nav-item" class="nav-link text-decoration-none" href="{{ base_url }}admin">Admin</a></li>
     {%- endif -%}
diff --git a/templates/page.html b/templates/page.html
index b47f638..e0cf0dd 100644
--- a/templates/page.html
+++ b/templates/page.html
@@ -36,7 +36,35 @@
   {% block scripts -%}
   {%- endblock %}
   <script>
-    var evtSourcesGlobal = {};
+    var evtSource = undefined;
+    var testCounter = 0;
+    let globalMaintenanceSystems = [];
+
+    function sseInit() {
+      let sseUrl = `${jhdata.base_url}api/sse`
+      if ( jhdata.user ) {
+          sseUrl = `${jhdata.base_url}api/sse/${jhdata.user}?_xsrf=${window.jhdata.xsrf_token}`;
+      }
+      if ( evtSource ) {
+        evtSource.close();
+      }
+      evtSource = new EventSource(sseUrl);
+      evtSource.onmessage = (e) => {
+        try {
+            const jsonData = JSON.parse(event.data);
+            for (const [key, value] of Object.entries(jsonData)) {
+                $(`[data-sse-${key}]`).trigger("sse", value);
+            }
+        } catch (error) {
+            console.error("Failed to parse SSE data:", error);
+        }
+      };
+      evtSource.onerror = (e) => {
+        console.log("Reconnect EventSource");
+        // Reconnect
+      }
+    }
+
     require.config({
       {%- if version_hash -%}
       urlArgs: "v={{version_hash}}",
@@ -123,11 +151,12 @@
   {% block script -%}
   {%- endblock %}
   <script>
+  $(document).ready(function() {
+    sseInit();
+  });
   window.onbeforeunload = function() {
     if (typeof evtSourcesGlobal !== 'undefined') {
-        for (const [key, value] of Object.entries(evtSourcesGlobal)) {
-            value.close();
-        }
+      evtSource.close();
     }
   }
   </script>
-- 
GitLab