fix(portal): extract inline JS to static file to resolve Qute {#raw} parse error
Deploy to Production / deploy (push) Successful in 3m5s
Deploy to Production / deploy (push) Successful in 3m5s
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -186,110 +186,7 @@ var __D = {dataJson.raw};
|
|||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
|
||||||
{#raw}
|
<script src="/dashboard.js"></script>
|
||||||
<script>
|
|
||||||
(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);
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
{/raw}
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user