From 8bc0ce1fb8f084d9356b6ebd077c0b7c40a51846 Mon Sep 17 00:00:00 2001 From: Carsten Rehfeld Date: Thu, 14 May 2026 16:01:07 +0200 Subject: [PATCH] fix(portal): extract inline JS to static file to resolve Qute {#raw} parse error Quarkus 3.15 removed the {#raw} section helper. Moving the dashboard map/expires JS to META-INF/resources/dashboard.js (served as static asset, bypasses Qute) fixes the portal startup failure in CI. Co-Authored-By: Mira --- .../resources/META-INF/resources/dashboard.js | 100 +++++++++++++++++ .../DashboardResource/dashboard.html | 105 +----------------- 2 files changed, 101 insertions(+), 104 deletions(-) create mode 100644 apix-portal/src/main/resources/META-INF/resources/dashboard.js diff --git a/apix-portal/src/main/resources/META-INF/resources/dashboard.js b/apix-portal/src/main/resources/META-INF/resources/dashboard.js new file mode 100644 index 0000000..95f658a --- /dev/null +++ b/apix-portal/src/main/resources/META-INF/resources/dashboard.js @@ -0,0 +1,100 @@ +(function () { + var data = window.__D || {}; + var visits = data.recentVisits || []; + var hasRegistrar = typeof data.registrarLat === 'number' && typeof data.registrarLon === 'number'; + + // ── Expires display ────────────────────────────────────────────────────── + var expiresEl = document.getElementById('expires-val'); + var expiresSub = document.getElementById('expires-sub'); + if (data.expiresAt && expiresEl) { + var d = new Date(data.expiresAt); + expiresEl.textContent = d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }); + var days = Math.ceil((d - Date.now()) / 86400000); + expiresSub.textContent = days > 0 ? days + ' days remaining' : 'expired'; + } + + // ── Map ────────────────────────────────────────────────────────────────── + var wrap = document.getElementById('map-wrap'); + var svg = d3.select('#world-map'); + var W = wrap.clientWidth || 800; + var H = Math.round(W * 0.48); + svg.attr('viewBox', '0 0 ' + W + ' ' + H) + .attr('width', W) + .attr('height', H); + + var projection = d3.geoNaturalEarth1() + .scale(W / 6.3) + .translate([W / 2, H / 2]); + var path = d3.geoPath(projection); + + // Ocean + svg.append('rect') + .attr('width', W).attr('height', H) + .attr('fill', '#060d18'); + + fetch('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json') + .then(function(r) { return r.json(); }) + .then(function(world) { + // Land + svg.append('path') + .datum(topojson.feature(world, world.objects.countries)) + .attr('d', path) + .attr('fill', '#111820') + .attr('stroke', '#1e2a38') + .attr('stroke-width', 0.5); + + // Graticule (faint grid) + svg.append('path') + .datum(d3.geoGraticule()()) + .attr('d', path) + .attr('fill', 'none') + .attr('stroke', '#0d1a28') + .attr('stroke-width', 0.4); + + // Agent blinks — resolve duplicates by approximate cell + var cellSize = 1.5; // degrees + var cells = {}; + visits.forEach(function(v) { + var cell = Math.round(v.lat / cellSize) + ',' + Math.round(v.lon / cellSize); + if (!cells[cell]) cells[cell] = { lat: v.lat, lon: v.lon, count: 0 }; + cells[cell].count++; + }); + var points = Object.values(cells); + points.forEach(function(pt, i) { + var pos = projection([pt.lon, pt.lat]); + if (!pos) return; + var r = Math.min(2 + Math.log1p(pt.count) * 1.5, 8); + svg.append('circle') + .attr('class', 'agent-blink') + .attr('cx', pos[0]) + .attr('cy', pos[1]) + .attr('r', r) + .style('animation-delay', (i * 190 % 5000) + 'ms'); + }); + + // Registrar star — drawn last so it sits on top + if (hasRegistrar) { + var pos = projection([data.registrarLon, data.registrarLat]); + if (pos) { + svg.append('text') + .attr('class', 'registrar-star') + .attr('x', pos[0]) + .attr('y', pos[1]) + .text('★'); + } + } + }) + .catch(function(e) { + console.warn('Map load failed:', e); + }); + + // ── Resize ──────────────────────────────────────────────────────────────── + window.addEventListener('resize', function() { + W = wrap.clientWidth || 800; + H = Math.round(W * 0.48); + svg.attr('viewBox', '0 0 ' + W + ' ' + H) + .attr('width', W).attr('height', H); + projection.scale(W / 6.3).translate([W / 2, H / 2]); + svg.selectAll('path').attr('d', path); + }); +})(); diff --git a/apix-portal/src/main/resources/templates/DashboardResource/dashboard.html b/apix-portal/src/main/resources/templates/DashboardResource/dashboard.html index ea9b1bb..5bd7932 100644 --- a/apix-portal/src/main/resources/templates/DashboardResource/dashboard.html +++ b/apix-portal/src/main/resources/templates/DashboardResource/dashboard.html @@ -186,110 +186,7 @@ var __D = {dataJson.raw}; -{#raw} - -{/raw} +