PageSourceSearch

https://tecdvr-v7.web.app/js/tec-desc-spaziale.js?v=20.374.20

js tecdvr-v7.web.app collected 2026-10-03 12:38:43 UTC 15,421 bytes, 386 lines download raw bytes

1/**
2 * tec-desc-spaziale.js — Modalità Descrizione Spaziale (MDS) Phase 1
3 * KB suggestion + override tecnologo; gating Tab 3 3D + DVR §1.3.1.
4 */
5(function (root) {
6  'use strict';
7  if (typeof root === 'undefined') return;
8
9  var VERSION = '1.1.0';
10
11  var MODALITA = {
12    NARRATIVA: 'narrativa',
13    MONOPIANO_GUIDATA: 'monopiano_guidata',
14    MULTIPIANO_GUIDATA: 'multipiano_guidata',
15    IBRIDA: 'ibrida'
16  };
17
18  var MODALITA_LABELS = {
19    narrativa: 'Narrativa — §1.3 testuale',
20    monopiano_guidata: 'Monopiano guidata — elenco compatto §1.3.1',
21    multipiano_guidata: 'Multipiano guidata — tabella 3D per piano',
22    ibrida: 'Ibrida — narrativa + tabella 3D'
23  };
24
25  function _ni(v, def) {
26    var n = parseInt(v, 10);
27    return isNaN(n) ? (def == null ? 0 : def) : n;
28  }
29
30  function _normalizeAteco(d) {
31    return String((d && d.atecoPrimario) || '').replace(/[\s.R\/]/g, '');
32  }
33
34  function _hotelArch() {
35    return root._tecHotelLuoghiArch || null;
36  }
37
38  function _edificioStruttura(d) {
39    d = d || {};
40    var H = _hotelArch();
41    if (H && typeof H.getEdificioStrutturaFields === 'function') {
42      return H.getEdificioStrutturaFields(d, d.ambientiSede || []);
43    }
44    return { nPianiFT: d.nPianiFT, nPianiInterrati: d.nPianiInterrati };
45  }
46
47  function _nPianiFT(d) {
48    var es = _edificioStruttura(d || {});
49    return _ni(es.nPianiFT, 1) || 1;
50  }
51
52  function _nPianiInterrati(d) {
53    var es = _edificioStruttura(d || {});
54    return _ni(es.nPianiInterrati, 0);
55  }
56
57  function _luogoForm() {
58    return root._tecLuogoTipoForm || null;
59  }
60
61  function _isSportWakeboard(d) {
62    return String((d && d.tipologiaAzienda) || '').toLowerCase() === 'sport_wakeboard';
63  }
64
65  function _hasOutdoorAquaticAmbiente(d) {
66    var L = _luogoForm();
67    var ambs = (d && d.ambientiSede) || [];
68    var i;
69    for (i = 0; i < ambs.length; i++) {
70      var amb = ambs[i] || {};
71      var tipo = amb.tipo || amb.tipoArea || '';
72      if (L && typeof L.isOutdoorAquaticProfile === 'function' && L.isOutdoorAquaticProfile(tipo)) return true;
73      var car = amb.caratteristiche || [];
74      if (car.indexOf('acquatico') >= 0 || car.indexOf('esterno') >= 0) return true;
75    }
76    return false;
77  }
78
79  function _hasBuildingProfile(d) {
80    var L = _luogoForm();
81    if (L && typeof L.getProfileForTipo === 'function') {
82      var ambs = (d && d.ambientiSede) || [];
83      var i;
84      for (i = 0; i < ambs.length; i++) {
85        if (L.getProfileForTipo(ambs[i] && ambs[i].tipo) === 'building') return true;
86      }
87    }
88    if (d && d.tipoEdificio) return true;
89    return false;
90  }
91
92  function _hasResortOutdoorMix(d) {
93    var L = _luogoForm();
94    if (!L || typeof L.getProfileForTipo !== 'function') return false;
95    var hasOutdoor = false;
96    var hasBuilding = false;
97    ((d && d.ambientiSede) || []).forEach(function (amb) {
98      var p = L.getProfileForTipo(amb && amb.tipo);
99      if (p === 'outdoor' || p === 'aquatic') hasOutdoor = true;
100      if (p === 'building') hasBuilding = true;
101    });
102    if (!hasOutdoor || !hasBuilding) return false;
103    var at = _normalizeAteco(d);
104    if (at.indexOf('55') === 0 || at.indexOf('56') === 0) return true;
105    return hasOutdoor && hasBuilding;
106  }
107
108  function suggestModalita(d) {
109    d = d || {};
110    var ateco = _normalizeAteco(d);
111    var nFt = _nPianiFT(d);
112    var nInt = _nPianiInterrati(d);
113
114    if (/cantiere_edile|impresa_edile|edilizia/i.test(String(d.tipologiaAzienda || d.settore || ''))
115        || ateco.indexOf('41') === 0 || ateco.indexOf('42') === 0 || ateco.indexOf('43') === 0) {
116      return {
117        modalita: MODALITA.NARRATIVA,
118        suggestedBy: 'kb:ateco_edilizia_cantiere',
119        perche: 'Impresa edile / general contractor — sede fissa + cantieri temporanei (§1.3 narrativo, non hotel).'
120      };
121    }
122
123    if (_isSportWakeboard(d)) {
124      return {
125        modalita: MODALITA.NARRATIVA,
126        suggestedBy: 'kb:tipologia_sport_wakeboard',
127        perche: 'Profilo sport acquatico / wakeboard — descrizione narrativa §1.3.'
128      };
129    }
130
131    if (_hasOutdoorAquaticAmbiente(d)) {
132      var L0 = _luogoForm();
133      if (L0) {
134        var ambs0 = d.ambientiSede || [];
135        var j;
136        for (j = 0; j < ambs0.length; j++) {
137          if (L0.isOutdoorAquaticProfile(ambs0[j] && ambs0[j].tipo)) {
138            return {
139              modalita: MODALITA.NARRATIVA,
140              suggestedBy: 'kb:outdoor_aquatic_profile',
141              perche: 'Ambiente outdoor/acquatico — priorità a §1.3 narrativo.'
142            };
143          }
144        }
145      }
146    }
147
148    if (ateco.indexOf('9311') === 0 && _hasOutdoorAquaticAmbiente(d)) {
149      return {
150        modalita: MODALITA.NARRATIVA,
151        suggestedBy: 'kb:ateco_9311_outdoor',
152        perche: 'ATECO 93.11 con aree outdoor — modalità narrativa.'
153      };
154    }
155
156    if (ateco.indexOf('55') === 0 && nFt >= 2) {
157      return {
158        modalita: MODALITA.MULTIPIANO_GUIDATA,
159        suggestedBy: 'kb:ateco_55_hotel',
160        perche: 'Struttura alberghiera multipiano — tabella 3D per piano.'
161      };
162    }
163
164    if (ateco.indexOf('85') === 0) {
165      return {
166        modalita: MODALITA.MULTIPIANO_GUIDATA,
167        suggestedBy: 'kb:ateco_85_school',
168        perche: 'Edificio scolastico — distribuzione per piano.'
169      };
170    }
171
172    if (_hasResortOutdoorMix(d)) {
173      return {
174        modalita: MODALITA.IBRIDA,
175        suggestedBy: 'kb:resort_outdoor_mix',
176        perche: 'Mix edificio + aree outdoor — narrativa e tabella 3D.'
177      };
178    }
179
180    if (nFt <= 1 && nInt <= 0 && _hasBuildingProfile(d)) {
181      return {
182        modalita: MODALITA.MONOPIANO_GUIDATA,
183        suggestedBy: 'kb:monopiano_building',
184        perche: 'Monopiano edificio — elenco ambienti compatto §1.3.1.'
185      };
186    }
187
188    if (nFt <= 1) {
189      return {
190        modalita: MODALITA.MONOPIANO_GUIDATA,
191        suggestedBy: 'kb:fallback_monopiano',
192        perche: 'Un solo piano fuori terra — default monopiano guidata.'
193      };
194    }
195
196    return {
197      modalita: MODALITA.MULTIPIANO_GUIDATA,
198      suggestedBy: 'kb:fallback_multipiano',
199      perche: 'Più piani fuori terra — default multipiano guidata.'
200    };
201  }
202
203  function getModalita(d) {
204    d = d || {};
205    if (d.descSpaziale && d.descSpaziale.modalita) return d.descSpaziale.modalita;
206    return suggestModalita(d).modalita;
207  }
208
209  function shouldShowSez131(d) {
210    d = d || {};
211    var m = getModalita(d);
212    var ds = d.descSpaziale || {};
213    if (m === MODALITA.NARRATIVA && ds.skipSez131) return false;
214    return true;
215  }
216
217  function shouldShowTab3D(d) {
218    return getModalita(d) !== MODALITA.NARRATIVA;
219  }
220
221  function shouldShowNarrativaNote(d) {
222    var m = getModalita(d);
223    return m === MODALITA.NARRATIVA || m === MODALITA.IBRIDA;
224  }
225
226  function getSez3DLabel(d) {
227    var m = getModalita(d);
228    if (m === MODALITA.NARRATIVA) return null;
229    if (m === MODALITA.MONOPIANO_GUIDATA) return 'Zone e ambienti';
230    return '3D — Distribuzione locali per piano';
231  }
232
233  function _esc(s) {
234    return String(s == null ? '' : s)
235      .replace(/&/g, '&amp;')
236      .replace(/</g, '&lt;')
237      .replace(/"/g, '&quot;');
238  }
239
240  function renderConfiguratorCard(d) {
241    d = d || {};
242    var cur = getModalita(d);
243    var sug = suggestModalita(d);
244    var ds = d.descSpaziale || {};
245    var skip = !!ds.skipSez131;
246    var showSkip = cur === MODALITA.NARRATIVA || cur === MODALITA.MONOPIANO_GUIDATA;
247    var opts = Object.keys(MODALITA_LABELS).map(function (k) {
248      return '<option value="' + k + '"' + (cur === k ? ' selected' : '') + '>' + _esc(MODALITA_LABELS[k]) + '</option>';
249    }).join('');
250
251    return '<div id="tec-mds-card" style="background:linear-gradient(135deg,#f0f9ff,#eff6ff);border:1.5px solid #93c5fd;border-radius:14px;padding:14px 18px;margin-bottom:14px">'
252      + '<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap">'
253      + '<span style="font-size:22px;line-height:1">🗺️</span>'
254      + '<div style="flex:1;min-width:220px">'
255      + '<p style="font-size:13px;font-weight:800;color:#1e3a8a;margin:0 0 4px">Modalità Descrizione Spaziale</p>'
256      + '<p style="font-size:11px;color:#475569;margin:0 0 10px;line-height:1.45">Suggerimento KB: <code style="font-size:10px;background:#dbeafe;padding:1px 6px;border-radius:4px">' + _esc(sug.suggestedBy) + '</code> — ' + _esc(sug.perche) + '</p>'
257      + '<label class="fl" style="font-size:10px;margin-bottom:4px">Modalità per questo DVR</label>'
258      + '<select id="tec-mds-modalita" data-tec-focus-key="tec-mds-modalita" onchange="window._tecDescSpaziale&&window._tecDescSpaziale._onModalitaChange(this)" style="width:100%;max-width:420px;border:1.5px solid #93c5fd;border-radius:8px;padding:8px 10px;font-size:12px;font-weight:600;background:#fff">'
259      + opts
260      + '</select>'
261      + (showSkip
262        ? '<label style="display:flex;align-items:center;gap:8px;margin-top:10px;font-size:11px;color:#334155;cursor:pointer">'
263          + '<input type="checkbox" id="tec-mds-skip131"' + (skip ? ' checked' : '') + ' onchange="window._tecDescSpaziale&&window._tecDescSpaziale._onSkip131Change(this)">'
264          + 'Ometti §1.3.1 nel DVR</label>'
265        : '')
266      + (ds.decidedAt ? '<p style="font-size:9px;color:#94a3b8;margin:8px 0 0">Deciso: ' + _esc(ds.decidedAt) + '</p>' : '')
267      + '</div></div></div>';
268  }
269
270  function _pianiBodyHtml(lv) {
271    lv = lv || function () { return []; };
272    var piani = lv('piani') || [];
273    var rows = piani.map(function (p, i) {
274      var nome = p.nome || ('Piano ' + (i === 0 ? 'Terra' : i));
275      var locali = (p.locali || []).length ? (p.locali || []).join(', ') : '—';
276      return '<div class="border rounded-xl p-4 bg-slate-50">'
277        + '<div class="mb-2"><span class="text-[10px] font-bold text-slate-400 uppercase tracking-wider">' + _esc(nome) + '</span></div>'
278        + '<div class="grid grid-cols-3 gap-3 mb-3 text-xs">'
279        + '<div><label class="fl">Nome piano</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.nome || '—') + '</div></div>'
280        + '<div><label class="fl">Destinazione</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.destinazione || '—') + '</div></div>'
281        + '<div><label class="fl">Note</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.note || '—') + '</div></div>'
282        + '</div>'
283        + '<div class="mb-2"><label class="fl mb-1">Locali presenti</label>'
284        + '<div style="font-size:11px;color:#475569">' + _esc(locali) + '</div></div></div>';
285    }).join('');
286    var empty = piani.length === 0
287      ? '<p class="text-sm text-amber-600 text-center py-4 bg-amber-50 rounded-xl">⚠️ Nessun piano sul Luogo Principale — aggiungili in Tab 4 Luoghi</p>'
288      : '';
289    return '<p class="text-xs text-slate-400 mb-3">Piani dal Luogo Principale (sola lettura).</p><div class="space-y-3">' + rows + empty + '</div>';
290  }
291
292  function renderPiani3DSection(d, ctx) {
293    d = d || {};
294    ctx = ctx || {};
295    var SEC = ctx.SEC;
296    if (!SEC || typeof SEC !== 'function') return '';
297
298    var parts = [];
299    if (shouldShowNarrativaNote(d)) {
300      var m = getModalita(d);
301      parts.push('<div style="background:#f8fafc;border:1.5px solid #e2e8f0;border-radius:12px;padding:12px 16px;margin-bottom:12px;font-size:12px;color:#475569">'
302        + '<i class="fas fa-align-left" style="color:#64748b;margin-right:6px"></i>'
303        + '<strong>§1.3 Descrizione ambienti</strong> — fonte primaria per la descrizione spaziale'
304        + (m === MODALITA.IBRIDA ? '; la tabella 3D resta disponibile sotto.' : ' (sezione 3D non ric
304hiesta in questa modalità).')
305        + '</div>');
306    }
307
308    if (!shouldShowTab3D(d)) return parts.join('');
309
310    var icons = ctx.sectorIcons || {};
311    var sectorKey = ctx.sectorKey || 'generic';
312    var icon = icons[sectorKey] || '🏢';
313    var label = getSez3DLabel(d) || '3D — Distribuzione locali per piano';
314    var lv = ctx.lv || function () { return []; };
315
316    parts.push(SEC('piani', icon, label, _pianiBodyHtml(lv)));
317    return parts.join('');
318  }
319
320  function applyModalita(modalita, skipSez131, onChange) {
321    var d = typeof root.D === 'function' ? root.D() : {};
322    var sug = suggestModalita(d);
323    var cur = (d && d.descSpaziale) || {};
324    var payload = {
325      modalita: modalita,
326      suggestedBy: cur.suggestedBy || sug.suggestedBy,
327      decidedAt: new Date().toISOString(),
328      skipSez131: !!skipSez131
329    };
330    if (typeof root.setDR === 'function') {
331      root.setDR('descSpaziale', payload);
332    } else if (typeof root.setD === 'function') {
333      root.setD('descSpaziale', payload);
334      if (typeof root.renderTab === 'function') root.renderTab();
335    }
336    if (typeof onChange === 'function') onChange(payload);
337    return payload;
338  }
339
340  function _onModalitaChange(sel) {
341    var skipEl = root.document && root.document.getElementById('tec-mds-skip131');
342    var m = sel && sel.value ? sel.value : getModalita(typeof root.D === 'function' ? root.D() : {});
343    applyModalita(m, skipEl ? skipEl.checked : false);
344  }
345
346  function _onSkip131Change(cb) {
347    var sel = root.document && root.document.getElementById('tec-mds-modalita');
348    var m = sel && sel.value ? sel.value : getModalita(typeof root.D === 'function' ? root.D() : {});
349    applyModalita(m, cb ? cb.checked : false);
350  }
351
352  function selfTest() {
353    var errs = [];
354    var wlake = {
355      tipologiaAzienda: 'sport_wakeboard',
356      atecoPrimario: '93.11.00',
357      ambientiSede: [{ tipo: 'specchio_acqua', caratteristiche: ['acquatico', 'esterno'] }]
358    };
359    if (suggestModalita(wlake).modalita !== MODALITA.NARRATIVA) errs.push('wlake→narrativa');
360    var modica = { atecoPrimario: '55.10.00', nPianiFT: '3' };
361    if (suggestModalita(modica).modalita !== MODALITA.MULTIPIANO_GUIDATA) errs.push('modica→multipiano');
362    var nar = { descSpaziale: { modalita: MODALITA.NARRATIVA, skipSez131: true } };
363    if (shouldShowSez131(nar)) errs.push('skipSez131');
364    if (getSez3DLabel({ descSpaziale: { modalita: MODALITA.MONOPIANO_GUIDATA } }) !== 'Zone e ambienti') errs.push('label monopiano');
365    if (getSez3DLabel({ descSpaziale: { modalita: MODALITA.NARRATIVA } }) !== null) errs.push('label narrativa hidden');
366    return { ok: !errs.length, errors: errs, VERSION: VERSION };
367  }
368
369  root._tecDescSpaziale = {
370    VERSION: VERSION,
371    MODALITA: MODALITA,
372    MODALITA_LABELS: MODALITA_LABELS,
373    suggestModalita: suggestModalita,
374    getModalita: getModalita,
375    shouldShowSez131: shouldShowSez131,
376    shouldShowTab3D: shouldShowTab3D,
377    shouldShowNarrativaNote: shouldShowNarrativaNote,
378    getSez3DLabel: getSez3DLabel,
379    renderConfiguratorCard: renderConfiguratorCard,
380    renderPiani3DSection: renderPiani3DSection,
381    applyModalita: applyModalita,
382    _onModalitaChange: _onModalitaChange,
383    _onSkip131Change: _onSkip131Change,
384    selfTest: selfTest
385  };
386})(typeof window !== 'undefined' ? window : globalThis);

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.