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