[FIX] Organigramma: guardia anti-ciclo (client+server) — "Nuovo Ruolo" bloccava il sito

Segnalazione Simon: in Struttura interna/Organigramma il clic su "Nuovo Ruolo"
blocca l'intero sito. Causa: né il tree builder server (OrgRoleController::buildTree,
$attach) né il render client (organigramma.js orgNodeHtml) avevano una guardia
anti-ciclo su parent_role_id → con una gerarchia ciclica la ricorsione va in loop
infinito (hang del backend / freeze del render).
- organigramma.js orgNodeHtml(node, seen): salta gli id già visti (rompe il ciclo);
  orgRender passa un Set condiviso. Live via bind-mount (buster 20260806).
- OrgRoleController::buildTree: $attach porta la catena antenati e non ridiscende su
  un id già visto. ⚠️ Richiede reload php-fpm host (opcache) per andare live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-08-25 18:00:08 +02:00
co-authored by Claude Opus 4.8
parent 0bf17db50a
commit bfda75b292
3 changed files with 24 additions and 8 deletions
+8 -3
View File
@@ -61,10 +61,15 @@ function orgRender() {
el('org-stat-vacant').textContent = vacant;
stats.style.display = 'flex';
tree.innerHTML = ORG.tree.map(orgNodeHtml).join('');
tree.innerHTML = ORG.tree.map(n => orgNodeHtml(n, new Set())).join('');
}
function orgNodeHtml(node) {
function orgNodeHtml(node, seen) {
// Guardia anti-ciclo: se un id ricompare nella catena il render si fermerebbe in loop
// infinito (il "blocco del sito" segnalato). Rompiamo il ciclo invece di ricorrere.
seen = seen || new Set();
if (!node || seen.has(node.id)) return '';
seen.add(node.id);
const gov = node.is_governance_body;
const holder = node.holder_name
? '<div class="org-holder">👤 ' + esc(node.holder_name) + '</div>'
@@ -72,7 +77,7 @@ function orgNodeHtml(node) {
const desc = node.description ? '<div class="org-desc">' + esc(node.description) + '</div>' : '';
const govBadge = gov ? '<span class="org-badge gov" title="Organo di amministrazione/direttivo (art. 23 D.Lgs. 138/2024)">Governance · Art. 23</span>' : '';
const children = (node.children && node.children.length)
? '<div class="org-children">' + node.children.map(orgNodeHtml).join('') + '</div>'
? '<div class="org-children">' + node.children.map(c => orgNodeHtml(c, seen)).join('') + '</div>'
: '';
return '' +