[FIX] Documenti Istituzionali (#499): le 5 voci standard non comparivano mai (NO_ORG)

Root cause: InstitutionalDocsController::list() usava requireAuth()+getCurrentOrgId(),
ma currentOrgId e' popolato solo da requireOrgAccess() → getCurrentOrgId() = null →
l'endpoint rispondeva sempre NO_ORG (400) → loadDocs riceveva standards=[] → la card
mostrava solo "Aggiungi voce" (screenshot Simon). L'endpoint NON aveva mai funzionato a runtime.
- Backend: list() usa resolveOrgId() (risolve X-Organization-Id senza lanciare) e restituisce
  SEMPRE le 5 voci standard; i documenti salvati solo se un'org e' selezionata.
- Frontend (dashboard.js): render condiviso + STD_FALLBACK → le 5 voci si mostrano sempre,
  anche se l'API fallisce. Buster dashboard.js 20260825.
- Backend attivato via reload php-fpm host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-08-26 18:14:07 +02:00
co-authored by Claude Opus 4.8
parent 69f3894c18
commit 396783cd41
3 changed files with 40 additions and 29 deletions
@@ -33,16 +33,18 @@ class InstitutionalDocsController extends BaseController
public function list(): void public function list(): void
{ {
$this->requireAuth(); $this->requireAuth();
$orgId = $this->getCurrentOrgId(); // BUGFIX (#499): usare requireAuth+getCurrentOrgId dava sempre NO_ORG (currentOrgId
if (!$orgId) { $this->jsonError('Nessuna organizzazione selezionata', 400, 'NO_ORG'); } // e' popolato solo da requireOrgAccess) → le 5 voci standard non comparivano MAI.
// Ora risolviamo l'org dall'header e restituiamo SEMPRE le 5 voci standard;
$rows = Database::fetchAll( // i documenti salvati si aggiungono solo se un'org e' selezionata.
$orgId = $this->resolveOrgId();
$rows = $orgId ? Database::fetchAll(
"SELECT id, doc_key, title, description, file_url, is_standard, sort_order, updated_at "SELECT id, doc_key, title, description, file_url, is_standard, sort_order, updated_at
FROM institutional_documents FROM institutional_documents
WHERE organization_id = ? WHERE organization_id = ?
ORDER BY is_standard DESC, sort_order ASC, id ASC", ORDER BY is_standard DESC, sort_order ASC, id ASC",
[$orgId] [$orgId]
); ) : [];
$this->jsonSuccess(['docs' => $rows, 'standards' => self::STANDARDS]); $this->jsonSuccess(['docs' => $rows, 'standards' => self::STANDARDS]);
} }
+1 -1
View File
@@ -118,7 +118,7 @@
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260731"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260806b"></script> <script src="/js/help.js?v=20260806b"></script>
<script src="/js/dashboard.js?v=20260724a"></script> <script src="/js/dashboard.js?v=20260825"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+22 -13
View File
@@ -318,24 +318,25 @@
for (var i = 0; i < standards.length; i++) if (standards[i].label === label) return standards[i].key; for (var i = 0; i < standards.length; i++) if (standards[i].label === label) return standards[i].key;
return null; return null;
} }
function loadDocs() { // Le 5 voci standard DEVONO essere sempre presenti anche se l'API non risponde.
var STD_FALLBACK = [
{ key: 'codice_etico', label: 'Codice Etico' },
{ key: 'mission', label: 'Mission' },
{ key: 'vision', label: 'Vision' },
{ key: 'statuto', label: 'Statuto' },
{ key: 'piano_sanzionatorio', label: 'Piano Sanzionatorio' }
];
function renderDocsCard(docs, standards) {
var card = document.getElementById('doc-card'); if (!card) return; var card = document.getElementById('doc-card'); if (!card) return;
setLoading('doc-card'); standards = (standards && standards.length) ? standards : STD_FALLBACK;
api.request('GET', '/institutional-docs/list').then(function (res) { docs = docs || [];
var data = (res && res.data) || res || {};
var docs = data.docs || [];
var standards = data.standards || [];
var byKey = {}; var customs = []; var byKey = {}; var customs = [];
docs.forEach(function (d) { docs.forEach(function (d) { if (d.doc_key) byKey[d.doc_key] = d; else customs.push(d); });
if (d.doc_key) byKey[d.doc_key] = d; else customs.push(d);
});
var html = ''; var html = '';
// 5 voci standard sempre presenti
standards.forEach(function (s) { standards.forEach(function (s) {
var item = byKey[s.key] || { doc_key: s.key, title: s.label, is_standard: 1 }; var item = byKey[s.key] || { doc_key: s.key, title: s.label, is_standard: 1 };
html += docRow(item, s.label); html += docRow(item, s.label);
}); });
// voci custom
customs.forEach(function (d) { html += docRow(d, null); }); customs.forEach(function (d) { html += docRow(d, null); });
if (canEditDocs()) { if (canEditDocs()) {
html += '<button class="btn-ghost doc-add" type="button" style="margin-top:8px">' + html += '<button class="btn-ghost doc-add" type="button" style="margin-top:8px">' +
@@ -344,8 +345,16 @@
card.innerHTML = '<div class="doc-list">' + html + '</div>'; card.innerHTML = '<div class="doc-list">' + html + '</div>';
refreshIcons(); refreshIcons();
wireDocEvents(card, standards); wireDocEvents(card, standards);
}).catch(function (e) { }
setError('doc-card', (e && e.message) || 'Riprova tra poco.'); function loadDocs() {
var card = document.getElementById('doc-card'); if (!card) return;
setLoading('doc-card');
api.request('GET', '/institutional-docs/list').then(function (res) {
var data = (res && res.data) || res || {};
renderDocsCard(data.docs || [], data.standards);
}).catch(function () {
// Anche in caso di errore mostriamo comunque le 5 voci standard.
renderDocsCard([], STD_FALLBACK);
}); });
} }
function docModal(opts, standards) { function docModal(opts, standards) {