PageSourceSearch

https://tecdvr-v7.web.app/js/tec-registro-azioni.js?v=20.374.20

js tecdvr-v7.web.app collected 2026-10-03 12:41:02 UTC 22,044 bytes, 387 lines download raw bytes

1/**
2 * tec-registro-azioni.js — IL REGISTRO OPERATIVO RISCHIO → AZIONE, E IL VERDETTO DEL TECNICO.
3 *
4 * ── LA RICHIESTA, NELLE SUE PAROLE ────────────────────────────────────────────────────────────
5 * Beniamino Macchiarola: «averla per il tecnico dentro il TecDVR, la più pratica, efficace ed
6 * efficiente possibile. Deve cioè elencare, SENZA DUBBI E SENZA MANCANZE, i rischi, la norma
7 * corrispondente, la gravità, le azioni suggerite, offrire un piano di esecuzione e la possibilità
8 * che quelle azioni siano ACCETTATE, RIFIUTATE, INTEGRATE O MODIFICATE dal tecnico».
9 *
10 * ── «SENZA DUBBI E SENZA MANCANZE» E' UN REQUISITO, NON UN AUSPICIO ──────────────────────────
11 * Percio' il registro parte dai RISCHI, non dalle azioni: ogni rischio valutato compare, anche
12 * quello per cui il tool non sa proporre nulla — e in quel caso lo DICHIARA («nessuna azione
13 * proposta») invece di sparire dall'elenco. Un registro che mostra solo le righe che ha saputo
14 * riempire non e' senza mancanze: e' senza le mancanze VISIBILI.
15 *
16 * ── I QUATTRO VERBI, E IL QUINTO STATO CHE NON E' UN VERBO ───────────────────────────────────
17 *   PROPOSTA    — il tool l'ha suggerita, il tecnico non si e' pronunciato. NON entra nel DVR.
18 *   ACCETTATA   — diventa una misura del Programma, come e' scritta.
19 *   RIFIUTATA   — non entra, e il rifiuto porta la sua motivazione: e' un atto tecnico, non un
20 *                 cestino. In audit «perche' non l'avete fatta» ha una risposta.
21 *   INTEGRATA   — accettata e arricchita: resta il testo del tool, piu' quello del tecnico.
22 *   MODIFICATA  — sostituita dal testo del tecnico: il tool cede, e lo dichiara.
23 *
24 * Il silenzio non e' consenso: una proposta non pronunciata non finisce nel pacchetto. E' la
25 * stessa regola dei servizi esterni verosimili e della sorveglianza sanitaria.
26 *
27 * ── NON SCRIVE ───────────────────────────────────────────────────────────────────────────────
28 * Produce cio' che andrebbe scritto e lo restituisce; scrive il chiamante. Cosi' la schermata puo'
29 * mostrare l'anteprima esatta di cio' che sta per accadere — la regola che il committente ha
30 * chiesto per tutti i pulsanti.
31 *
32 * Espone window._tecRegistroAzioni.
33 */
34(function (win) {
35  'use strict';
36  if (typeof win === 'undefined') return;
37
38  var VERSION = '20.373.134';
39
40  var VERDETTI = [
41    { id: 'proposta',   label: 'Da decidere',  colore: '64748B', nelDvr: false,
42      spiega: 'Il tool l’ha suggerita. Finché non ti pronunci non entra nel DVR.' },
43    { id: 'accettata',  label: 'Accettata',    colore: '16A34A', nelDvr: true,
44      spiega: 'Entra nel Programma di Miglioramento con il testo del tool.' },
45    { id: 'rifiutata',  label: 'Rifiutata',    colore: 'DC2626', nelDvr: false, motivoObbligatorio: true,
46      spiega: 'Non entra. La motivazione resta nel tool: in audit «perché non l’avete fatta» ha una risposta.' },
47    { id: 'integrata',  label: 'Integrata',    colore: '2563EB', nelDvr: true, testoAggiuntivo: true,
48      spiega: 'Entra con il testo del tool più il tuo.' },
49    { id: 'modificata', label: 'Modificata',   colore: 'B45309', nelDvr: true, testoSostitutivo: true,
50      spiega: 'Entra con il tuo testo al posto di quello del tool.' }
51  ];
52  function verdettoMeta(id) {
53    for (var i = 0; i < VERDETTI.length; i++) if (VERDETTI[i].id === id) return VERDETTI[i];
54    return VERDETTI[0];
55  }
56
57  function _s(v) { return String(v == null ? '' : v).trim(); }
58  function _n(v) { var x = Number(v); return isNaN(x) ? 0 : x; }
59  function _arr(v) { return Array.isArray(v) ? v : []; }
60
61  /**
62   * La chiave di un'azione proposta. Deve restare la stessa fra due aperture della schermata,
63   * altrimenti il verdetto del tecnico si stacca dalla propria azione e sembra sparito.
64   * Non si usa l'indice nell'elenco: basta che il motore riordini e i verdetti si mescolano.
65   */
66  function chiaveAzione(p) {
67    return [_s(p && p.rischioCod), _s(p && p.mansioneId), _s(p && p.luogoId), _s(p && p.tipo),
68            _s(p && p.descrizione).slice(0, 60).toLowerCase().replace(/\s+/g, ' ')].join('|');
69  }
70
71  function _fascia(r) {
72    var D = win._tecDashboardRischi;
73    if (D && typeof D.fasciaDi === 'function') return D.fasciaDi(r);
74    return { id: 'basso', label: 'Basso', colore: '16A34A' };
75  }
76
77  /**
78   * IL REGISTRO. Una riga per RISCHIO VALUTATO — tutti, anche quelli senza proposte.
79   */
80  function registro(d) {
81    d = d || {};
82    var MOT = win._tecMotore, AZ = win._tecAzioniDaResiduo;
83    if (!MOT || typeof MOT.righeMatriceDvr !== 'function') {
84      return { disponibile: false, perche: 'il motore dei rischi non è caricato', righe: [] };
85    }
86    var righeM = [];
87    try { righeM = _arr(MOT.righeMatriceDvr(d)); } catch (_e) {
88      return { disponibile: false, perche: 'la matrice dei rischi non è calcolabile', righe: [] };
89    }
90    var proposte = [];
91    if (AZ && typeof AZ.proponi === 'function') {
92      try { proposte = _arr(AZ.proponi(d)); } catch (_e2) { proposte = []; }
93    }
94    var verdetti = (d && d.verdettiAzioni) || {};
95
96    var perRischio = {};
97    proposte.forEach(function (p) {
98      var k = _s(p.rischioCod) + '|' + _s(p.mansioneId);
99      (perRischio[k] = perRischio[k] || []).push(p);
100    });
101
102    var righe = righeM.map(function (x) {
103      var cod = _s(x[1]), mans = _s(x[0]);
104      var ri = _n(x[6]), rr = _n(x[7]) || ri;
105      var f = _fascia(rr);
106      var mie = [];
107      Object.keys(perRischio).forEach(function (k) {
108        if (k.indexOf(cod + '|') === 0) mie = mie.concat(perRischio[k]);
109      });
110      var az = mie.map(function (p) {
111        var ch = chiaveAzione(p);
112        var v = verdetti[ch] || {};
113        var stato = _s(v.verdetto) || 'proposta';
114        var meta = verdettoMeta(stato);
115        return {
116          chiave: ch, tipo: _s(p.tipo), descrizione: _s(p.descrizione), norma: _s(p.norma),
117          responsabile: _s(v.responsabile) || _s(p.responsabile),
118          scadenza: _s(v.scadenza) || _s(p.scadenza),
119          rResiduoPre: _n(p.rResiduoPre), rAtteso: _n(p.rAtteso),
120          riduzioneAttesa: _n(p.rResiduoPre) - _n(p.rAtteso),
121          verdetto: stato, verdettoLabel: meta.label, verdettoColore: meta.colore,
122          nelDvr: !!meta.nelDvr, motivo: _s(v.motivo), testoTecnico: _s(v.testo),
123          decisaIl: _s(v.il)
124        };
125      });
126      return {
127        cod: cod, nome: _s(x[2]), mansione: mans, fonte: _s(x[3]),
128        P: _n(x[4]), D: _n(x[5]), R: ri, residuo: rr,
129        fascia: f.label, fasciaColore: f.colore, norma: _s(x[9]),
130        misureEsistenti: _s(x[8]),
131        azioni: az,
132        nProposte: az.filter(function (a) { return a.verdetto === 'proposta'; }).length,
133        nAccolte: az.filter(function (a) { return a.nelDvr; }).length,
134        senzaProposte: az.length === 0,
135        // «senza dubbi e senza mancanze»: un rischio sopra soglia senza alcuna azione accolta
136        // e' una lacuna, e va detta anche quando il tool non sa proporre niente.
137        scoperto: rr >= 6 && az.filter(function (a) { return a.nelDvr; }).length === 0
138      };
139    });
140    righe.sort(function (a, b) { return b.residuo - a.residuo || b.R - a.R; });
141    return {
142      disponibile: true, norma: 'art. 28 c.2 lett. b, c e d D.Lgs. 81/08',
143      righe: righe,
144      nRischi: righe.length,
145      nSenzaProposte: righe.filter(function (r) { return r.senzaProposte; }).length,
146      nScoperti: righe.filter(function (r) { return r.scoperto; }).length,
147      nDaDecidere: righe.reduce(function (s, r) { return s + r.nProposte; }, 0),
148      nAccolte: righe.reduce(function (s, r) { return s + r.nAccolte; }, 0)
149    };
150  }
151
152  /**
153   * IL PIANO DI ESECUZIONE: solo cio' che il tecnico ha accolto, ordinato per urgenza.
154   * Chi non ha responsabile o data viene messo in testa: e' un impegno che non impegna nessuno.
155   */
156  function piano(d) {
157    var reg = registro(d);
158    if (!reg.disponibile) return { disponibile: false, perche: reg.perche, voci: [] };
159    var voci = [];
160    reg.righe.forEach(function (r) {
161      r.azioni.forEach(function (a) {
162        if (!a.nelDvr) return;
163        voci.push({
164          rischio: r.cod + ' — ' + r.nome, mansione: r.mansione, fascia: r.fascia,
165          residuo: r.residuo, riduzioneAttesa: a.riduzioneAttesa,
166          azione: a.verdetto === 'modificata' ? a.testoTecnico
167                : (a.verdetto === 'integrata' ? a.descrizione + ' — ' + a.testoTecnico : a.descrizione),
168          tipo: a.tipo, norma: a.norma,
169          responsabile: a.responsabile, scadenza: a.scadenza,
170          incompleta: !a.responsabile || !a.scadenza,
171          verdetto: a.verdetto
172        });
173      });
174    });
175    voci.sort(function (a, b) {
176      if (a.incompleta !== b.incompleta) return a.incompleta ? -1 : 1;
177      return b.residuo - a.residuo;
178    });
179    return {
180      disponibile: true, norma: 'art. 28 c.2 lett. c D.Lgs. 81/08',
181      voci: voci, totale: voci.length,
182      incomplete: voci.filter(function (v) { return v.incompleta; }).length,
183      riduzioneAttesaTotale: voci.reduce(function (s, v) { return s + _n(v.riduzioneAttesa); }, 0)
184    };
185  }
186
187  /**
188   * Il verdetto del tecnico, tradotto in cio' che andrebbe scritto. NON scrive.
189   * @param scelta {verdetto, motivo, testo, responsabile, scadenza, il}
190   */
191  function verdettoDaScrivere(d, chiave, scelta) {
192    scelta = scelta || {};
193    var meta = verdettoMeta(_s(scelta.verdetto));
194    var errori = [];
195    if (meta.motivoObbligatorio && !_s(scelta.motivo)) {
196      errori.push('Un rifiuto senza motivazione non è un atto tecnico: in audit «perché non l’avete fatta» resterebbe senza risposta.');
197    }
198    if ((meta.testoAggiuntivo || meta.testoSostitutivo) && !_s(scelta.testo)) {
199      errori.push('Hai scelto «' + meta.label + '» ma non hai scritto il testo: senza, la scelta non ha contenuto.');
200    }
201    if (errori.length) return { ok: false, errori: errori, scrivi: null };
202    var riga = {};
203    riga[_s(chiave)] = {
204      verdetto: meta.id, motivo: _s(scelta.motivo), testo: _s(scelta.testo),
205      responsabile: _s(scelta.responsabile), scadenza: _s(scelta.scadenza), il: _s(scelta.il)
206    };
207    return { ok: true, errori: [], chiave: 'verdettiAzioni', scrivi: riga, nelDvr: !!meta.nelDvr };
208  }
209
210  function selfTest() {
211    var e = [];
212    if (VERDETTI.length !== 5) e.push('i verbi del tecnico non sono piu cinque');
213    if (verdettoMeta('proposta').nelDvr) e.push('una proposta non pronunciata entrerebbe nel DVR: il silenzio non e consenso');
214    if (!verdettoMeta('accettata').nelDvr) e.push('un azione accettata non entra nel DVR');
215    if (verdettoMeta('rifiutata').nelDvr) e.push('un azione rifiutata entrerebbe comunque nel DVR');
216    if (!verdettoMeta('integrata').nelDvr || !verdettoMeta('modificata').nelDvr) e.push('integrata o modificata non entrano nel DVR');
217
218    var k1 = chiaveAzione({ rischioCod: 'B10', mansioneId: 'm1', tipo: 'tecnica', descrizione: 'Pavimenti antisdrucciolevoli' });
219    var k2 = chiaveAzione({ rischioCod: 'B10', mansioneId: 'm1', tipo: 'tecnica', descrizione: 'Pavimenti  antisdrucciolevoli' });
220    if (k1 !== k2) e.push('la chiave cambia per uno spazio doppio: il verdetto si staccherebbe dalla propria azione');
221    if (chiaveAzione({ rischioCod: 'B10', tipo: 'dpi', descrizione: 'X' }) === k1) e.push('due azioni diverse hanno la stessa chiave');
222
223    var r = verdettoDaScrivere({}, 'k', { verdetto: 'rifiutata' });
224    if (r.ok) e.push('un rifiuto senza motivazione viene accettato');
225    var r2 = verdettoDaScrivere({}, 'k', { verdetto: 'rifiutata', motivo: 'gia attuata da anni' });
226    if (!r2.ok) e.push('un rifiuto motivato viene respinto');
227    if (r2.nelDvr) e.push('un rifiuto motivato finirebbe nel DVR');
228    var r3 = verdettoDaScrivere({}, 'k', { verdetto: 'modificata' });
229    if (r3.ok) e.push('una modifica senza testo viene accettata');
230    var r4 = verdettoDaScrivere({}, 'k', { verdetto: 'accettata' });
231    if (!r4.ok || !r4.nelDvr) e.push('un accettazione non produce nulla');
232    if (r4.scrivi && r4.scrivi.k && r4.scrivi.k.verdetto !== 'accettata') e.push('il verdetto scritto non e quello scelto');
233
234    // il caso «motore assente» si prova nel gate, che puo' costruire una finestra nuda: qui il
235    // motore c'e' quasi sempre, e pretendere il contrario darebbe un selfTest che mente.
236    var reg = registro({});
237    if (reg.disponibile) {
238      if (!Array.isArray(reg.righe)) e.push('il registro non restituisce un elenco di righe');
239      if (typeof reg.nScoperti !== 'number') e.push('il registro non conta i rischi scoperti');
240    } else if (!_s(reg.perche)) {
241      e.push('il registro si dichiara non disponibile senza dire perche');
242    }
243    return { ok: e.length === 0, errors: e };
244  }
245
246  win._tecRegistroAzioni = {
247    VERSION: VERSION, VERDETTI: VERDETTI, verdettoMeta: verdettoMeta,
248    chiaveAzione: chiaveAzione, registro: registro, piano: piano,
249    verdettoDaScrivere: verdettoDaScrivere, selfTest: selfTest
250  };
251})(typeof window !== 'undefined' ? window : this);
252
253/**
254 * IL DISEGNO DEL REGISTRO. Una riga per rischio, le azioni dentro, e i quattro verbi a portata di
255 * pollice. Nessun «vai»: tutto cio' che riguarda un rischio sta nella sua riga.
256 * Le righe scoperte e quelle senza proposte stanno in TESTA, perche' sono le mancanze — e un
257 * registro «senza mancanze» deve mostrarle per prime, non nasconderle in fondo.
258 */
259(function (win) {
260  'use strict';
261  var A = win._tecRegistroAzioni;
262  if (!A) return;
263  function _e(s) {
264    return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) {
265      return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c];
266    });
267  }
268  var NAVY = '#1e3a5f', GRIGIO = '#64748b';
269
270  function _bottoni(a, onclickPrefix) {
271    var h = '<div style="display:flex;gap:5px;flex-wrap:wrap;margin-top:7px">';
272    A.VERDETTI.filter(function (v) { return v.id !== 'proposta'; }
272).forEach(function (v) {
273      var on = a.verdetto === v.id;
274      h += '<button type="button" title="' + _e(v.spiega) + '"'
275        + (onclickPrefix ? ' onclick="' + onclickPrefix + '(\'' + _e(a.chiave) + '\',\'' + v.id + '\')"' : '')
276        + ' style="cursor:pointer;border:1.5px solid ' + (on ? '#' + v.colore : '#e2e8f0')
277        + ';background:' + (on ? '#' + v.colore : '#fff') + ';color:' + (on ? '#fff' : '#475569')
278        + ';border-radius:7px;font-size:10.5px;font-weight:800;padding:5px 10px;font-family:inherit">'
279        + _e(v.label) + '</button>';
280    });
281    return h + '</div>';
282  }
283
284  function html(d, opts) {
285    opts = opts || {};
286    var reg = opts.registro || A.registro(d);
287    if (!reg.disponibile) {
288      return '<div style="font-size:11.5px;color:' + GRIGIO + ';font-style:italic">Registro non disponibile: '
289        + _e(reg.perche) + '.</div>';
290    }
291    var pre = opts.onclick || '';
292    var h = '<div style="border:1px solid #e2e8f0;border-radius:14px;background:#fff;overflow:hidden">';
293    h += '<div style="padding:13px 16px;border-top:3px solid ' + (reg.nScoperti ? '#dc2626' : '#4338ca')
294      + ';border-bottom:1px solid #e2e8f0">'
295      + '<div style="font-size:9.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:' + GRIGIO + '">'
296      + _e(reg.norma) + '</div>'
297      + '<div style="font-size:14.5px;font-weight:800;color:' + NAVY + ';margin-top:3px">Registro rischio → azione</div>'
298      + '<div style="font-size:11px;color:' + GRIGIO + ';margin-top:2px">'
299      + reg.nRischi + ' rischi valutati · <b>' + reg.nDaDecidere + ' azioni da decidere</b> · '
300      + reg.nAccolte + ' accolte'
301      + (reg.nScoperti ? ' · <b style="color:#dc2626">' + reg.nScoperti + ' sopra soglia senza alcuna azione</b>' : '')
302      + (reg.nSenzaProposte ? ' · ' + reg.nSenzaProposte + ' senza proposte del tool' : '')
303      + '</div></div>';
304
305    var ordinate = reg.righe.slice().sort(function (a, b) {
306      var pa = a.scoperto ? 0 : (a.senzaProposte ? 1 : 2);
307      var pb = b.scoperto ? 0 : (b.senzaProposte ? 1 : 2);
308      return pa - pb || b.residuo - a.residuo;
309    });
310
311    ordinate.forEach(function (r) {
312      h += '<div style="padding:12px 16px;border-top:1px solid #f1f5f9'
313        + (r.scoperto ? ';background:#fff7f7' : '') + '">';
314      h += '<div style="display:flex;gap:10px;align-items:baseline;flex-wrap:wrap">'
315        + '<span style="font-size:9.5px;font-weight:900;color:#fff;background:#' + r.fasciaColore
316        + ';border-radius:4px;padding:2px 7px">' + _e(r.fascia) + '</span>'
317        + '<span style="font-size:10px;font-weight:900;color:' + GRIGIO + '">' + _e(r.cod) + '</span>'
318        + '<span style="font-size:13px;font-weight:800;color:' + NAVY + '">' + _e(r.nome) + '</span>'
319        + '<span style="font-size:10.5px;color:' + GRIGIO + '">P' + r.P + ' · D' + r.D
320        + ' · R ' + r.R + ' → residuo <b>' + r.residuo + '</b></span>'
321        + '<span style="margin-left:auto;font-size:10px;color:#94a3b8">' + _e(r.mansione) + '</span></div>';
322      h += '<div style="font-size:10px;color:' + GRIGIO + ';margin-top:3px"><b>Norma:</b> ' + _e(r.norma) + '</div>';
323
324      if (r.senzaProposte) {
325        h += '<div style="margin-top:8px;padding:9px 12px;background:#fffbeb;border:1px solid #fde68a;border-radius:9px;'
326          + 'font-size:11px;color:#92400e">Il tool non sa proporre alcuna azione per questo rischio. '
327          + '<b>Non significa che non serva:</b> significa che la misura la devi scrivere tu.'
328          + (pre ? ' <button type="button" onclick="' + pre + '(\'' + _e(r.cod) + '\',\'nuova\')" style="cursor:pointer;border:1px solid #d97706;background:#fff;color:#b45309;border-radius:7px;font-size:10.5px;font-weight:800;padding:4px 10px;margin-left:6px;font-family:inherit">Scrivi una misura</button>' : '')
329          + '</div>';
330      }
331      r.azioni.forEach(function (a) {
332        h += '<div style="margin-top:8px;padding:9px 12px;border:1px solid #e2e8f0;border-radius:9px;background:#fcfdff">'
333          + '<div style="display:flex;gap:8px;align-items:baseline;flex-wrap:wrap">'
334          + '<span style="font-size:9px;font-weight:900;text-transform:uppercase;letter-spacing:.05em;
334color:#fff;background:#'
335          + a.verdettoColore + ';border-radius:4px;padding:2px 6px">' + _e(a.verdettoLabel) + '</span>'
336          + '<span style="font-size:9.5px;font-weight:800;color:' + GRIGIO + ';text-transform:uppercase">' + _e(a.tipo) + '</span>'
337          + (a.riduzioneAttesa > 0
338              ? '<span style="font-size:10px;font-weight:800;color:#16a34a">riduce ' + a.rResiduoPre + ' → ' + a.rAtteso + '</span>'
339              : '<span style="font-size:10px;color:#94a3b8">nessuna riduzione attesa dichiarata</span>')
340          + '</div>'
341          + '<div style="font-size:12px;color:#1e293b;line-height:1.45;margin-top:4px">' + _e(a.descrizione) + '</div>'
342          + (a.norma ? '<div style="font-size:10px;color:' + GRIGIO + ';margin-top:2px">' + _e(a.norma) + '</div>' : '')
343          + (a.motivo ? '<div style="font-size:10.5px;color:#991b1b;margin-top:4px"><b>Rifiutata perché:</b> ' + _e(a.motivo) + '</div>' : '')
344          + (a.testoTecnico ? '<div style="font-size:10.5px;color:#1d4ed8;margin-top:4px"><b>Tuo testo:</b> ' + _e(a.testoTecnico) + '</div>' : '')
345          + _bottoni(a, pre)
346          + '</div>';
347      });
348      h += '</div>';
349    });
350    return h + '</div>';
351  }
352
353  /** Il piano di esecuzione, disegnato. Le voci senza responsabile o data stanno in testa. */
354  function htmlPiano(d, opts) {
355    opts = opts || {};
356    var p = opts.piano || A.piano(d);
357    if (!p.disponibile) return '';
358    if (!p.totale) {
359      return '<div style="border:1px solid #e2e8f0;border-radius:14px;background:#fff;padding:15px 17px">'
360        + '<div style="font-size:14px;font-weight:800;color:' + NAVY + '">Piano di esecuzione</div>'
361        + '<div style="font-size:11.5px;color:' + GRIGIO + ';margin-top:4px">Nessuna azione è ancora stata accolta: '
362        + 'il piano si popola man mano che ti pronunci sulle proposte qui sopra.</div></div>';
363    }
364    var h = '<div style="border:1px solid #e2e8f0;border-radius:14px;background:#fff;overflow:hidden">'
365      + '<div style="padding:13px 16px;border-top:3px solid #16a34a;border-bottom:1px solid #e2e8f0">'
366      + '<div style="font-size:9.5px;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:' + GRIGIO + '">'
367      + _e(p.norma) + '</div>'
368      + '<div style="font-size:14.5px;font-weight:800;color:' + NAVY + ';margin-top:3px">Piano di esecuzione</div>'
369      + '<div style="font-size:11px;color:' + GRIGIO + ';margin-top:2px">' + p.totale + ' azioni accolte'
370      + (p.incomplete ? ' · <b style="color:#d97706">' + p.incomplete + ' senza responsabile o data</b>' : '')
371      + ' · riduzione attesa complessiva <b>' + p.riduzioneAttesaTotale + ' punti</b></div></div>';
372    p.voci.forEach(function (v) {
373      h += '<div style="padding:10px 16px;border-top:1px solid #f1f5f9' + (v.incompleta ? ';background:#fffbeb' : '') + '">'
374        + '<div style="font-size:12px;font-weight:700;color:#1e293b">' + _e(v.azione) + '</div>'
375        + '<div style="font-size:10px;color:' + GRIGIO + ';margin-top:3px">'
376        + _e(v.rischio) + ' · ' + _e(v.mansione) + ' · ' + _e(v.norma) + '</div>'
377        + '<div style="font-size:10.5px;margin-top:3px;color:' + (v.incompleta ? '#b45309' : GRIGIO) + '">'
378        + '<b>Chi:</b> ' + _e(v.responsabile || '— da indicare') + ' &nbsp;·&nbsp; <b>Entro:</b> ' + _e(v.scadenza || '— da indicare')
379        + (v.incompleta ? ' &nbsp;—&nbsp; un impegno senza nome e senza data non impegna nessuno' : '')
380        + '</div></div>';
381    });
382    return h + '</div>';
383  }
384
385  A.html = html;
386  A.htmlPiano = htmlPiano;
387})(typeof window !== 'undefined' ? window : this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.