js/Calc_summary.js.bak_20260428_192121_cleanup

Code: DEV-49F7D646 Size: 42.8 KB Lines: 829 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/Calc_summary.js.bak_20260428_192121_cleanup

Task / Comment

Open report form
// Calc_summary.js — Kalkylsammanställning (egen sida)
// Läser _kalkylItems (låst varukorg). Ögonikon = expand tillval/variant.

var _calcSummaryExpanded = {};
var _calcSummaryCatExpanded = {}; // category-level expand-all toggle
var _calcSummaryOrigin = null;
var _calcSummaryState = { owners: 1, deductType: 'none', sliderValue: null, finYears: 15 };

function _calcSummaryEsc(v){
    return String(v == null ? '' : v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}

function _calcSummaryNukeOthers(){
    // Döljer HELA alla andra page-content (inklusive page-konfigurator)
    document.querySelectorAll('.page-content').forEach(function(p){
        if(p.id !== 'page-calc-summary'){
            p.classList.remove('active');
            p.style.display = 'none';
        }
    });
    // Döljer kända inre vyer som kan leaka — men TAR INTE BORT newCalcRenderArea
    // så att tillbaka-knappen kan återställa fönster/produkt-konfigurator-STATE.
    ['summaryProductList','kalkylListView','kalkylConfigView','newCalcRenderArea','newCalcShell','cfgCategoryBar','cfgCategoryGrid','affarView','solarConfigView','genericConfigView','fonsterConfigView','batteriConfigView','laddboxConfigView','taktvatConfigView','varmepumpConfigView','takConfigView','isoleringConfigView'].forEach(function(id){
        var el = document.getElementById(id);
        if(el){ el.style.display = 'none'; el.classList.remove('active'); }
    });
}

function openCalcSummary(){
    console.log('[CalcSummary] openCalcSummary()');
    // Spara origin-sidan så Tillbaka-knappen kan återställa den
    var active = document.querySelector('.page-content.active');
    if(active && active.id !== 'page-calc-summary'){
        _calcSummaryOrigin = active.id;
    }
    _calcSummaryNukeOthers();
    var page = document.getElementById('page-calc-summary');
    if(!page){
        console.error('[CalcSummary] page-calc-summary saknas i DOM — är pages/Calc_summary.php inkluderad?');
        return;
    }
    page.classList.add('active');
    page.style.display = 'block';
    _calcSummaryLoadQuote();
    _calcSummaryRenderHeader();
    // Re-trigga kategori-add-ons (för sparade kalkyler som öppnas, eller efter cache-invalidate)
    if(typeof _prisInvalidateAddonsCache === 'function') _prisInvalidateAddonsCache();
    if(typeof _prisRecalculateAddons === 'function') _prisRecalculateAddons();
    renderCalcSummary();
    try { window.scrollTo(0, 0); } catch(e){}
}

function closeCalcSummary(){
    var page = document.getElementById('page-calc-summary');
    if(page){ page.classList.remove('active'); page.style.display = 'none'; }
    var originId = _calcSummaryOrigin || 'page-konfigurator';
    var origin = document.getElementById(originId);
    if(origin){ origin.classList.add('active'); origin.style.display = 'block'; }
    if(originId === 'page-konfigurator'){
        var renderArea = document.getElementById('newCalcRenderArea');
        var cfgView = document.getElementById('kalkylConfigView');
        var listView = document.getElementById('kalkylListView');
        // Om användaren hade en aktiv konfigurator (render-area finns med innehåll) →
        // återvisa den så fönsterkalkylen m.m. står kvar med alla val intakta.
        if(renderArea && renderArea.innerHTML && renderArea.innerHTML.trim() !== ''){
            if(cfgView) cfgView.style.display = 'block';
            if(listView) listView.style.display = 'none';
            renderArea.style.display = 'block';
        } else {
            // Ingen pågående konfigurator → visa kalkyl-listan som vanligt
            if(listView) listView.style.display = 'block';
            if(cfgView) cfgView.style.display = 'none';
            if(typeof currentQuoteId !== 'undefined') try{ currentQuoteId = null; }catch(e){}
        }
    }
    document.querySelectorAll('.nav-item').forEach(function(n){
        var targetPage = originId.replace(/^page-/, '');
        if(n.dataset && n.dataset.page === targetPage) n.classList.add('active');
        else n.classList.remove('active');
    });
    _calcSummaryOrigin = null;
}

function toggleCalcSummaryRow(entryId){
    _calcSummaryExpanded[entryId] = !_calcSummaryExpanded[entryId];
    renderCalcSummary();
}

function toggleCalcSummaryCat(cat){
    _calcSummaryCatExpanded[cat] = !_calcSummaryCatExpanded[cat];
    // När kategori expanderas → expandera alla entries i den
    var open = _calcSummaryCatExpanded[cat];
    if(typeof _kalkylItems !== 'undefined' && _kalkylItems[cat]){
        var entries = Array.isArray(_kalkylItems[cat].entries) ? _kalkylItems[cat].entries : [_kalkylItems[cat]];
        entries.forEach(function(e){ if(e && e.entry_id) _calcSummaryExpanded[e.entry_id] = open; });
    }
    renderCalcSummary();
}
window.toggleCalcSummaryCat = toggleCalcSummaryCat;

function _calcSummaryCatLabel(cat){
    var labels = typeof _catLabels !== 'undefined' ? _catLabels
        : {solceller:'Solpaneler',batteri:'Batteri',laddbox:'Laddbox',taktvatt:'Taktv\u00e4tt',varmepump:'V\u00e4rmepump',tak:'Tak',fonster:'F\u00f6nster',isolering:'Isolering'};
    if(cat === '_addons') return 'Övriga installationskostnader';
    return labels[cat] || cat;
}

// Grupperar entries hierarkiskt efter info-tillval med ordning 1 (yttre) → 2 (inre) → …
// Ett info-tillval har tillval[i].isInfo===true och .ordning (1/2/3) + .value (t.ex. "Våning 1").
// Entries utan info-tillval hamnar under "(Övrigt)" eller direkt.
function _calcSummaryRenderGrouped(cat, entries){
    // Gruppering sker ENDAST om ett tillval har unit='info' och ordning = 1 eller 2.
    // ordning 1 = yttre grupp (t.ex. Våning), ordning 2 = inre grupp (t.ex. Rum).
    // Obligatoriska tillval utan info/ordning är ren metadata och skapar INGEN gruppering.
    function _ord(tv){
        if(!tv || !tv.isInfo || tv.ordning == null) return null;
        var o = parseInt(tv.ordning);
        if(o === 1 || o === 2) return o;
        return null;
    }
    function infoKey(e, ord){
        var arr = Array.isArray(e.tillval) ? e.tillval : [];
        var hit = arr.find(function(tv){ return _ord(tv) === ord; });
        return hit ? String(hit.value || '') : '';
    }
    // Samla alla unika ordning-nivåer
    var levels = [];
    entries.forEach(function(e){
        (Array.isArray(e.tillval) ? e.tillval : []).forEach(function(tv){
            var o = _ord(tv);
            if(o != null && levels.indexOf(o) === -1) levels.push(o);
        });
    });
    levels.sort(function(a,b){ return a - b; });
    if(!levels.length){
        return entries.map(function(e){ return _calcSummaryRenderEntry(cat, e); }).join('');
    }
    // Rekursiv rendering
    function renderLevel(list, levelIdx){
        if(levelIdx >= levels.length){
            return list.map(function(e){ return _calcSummaryRenderEntry(cat, e); }).join('');
        }
        var ord = levels[levelIdx];
        var isLast = (levelIdx === levels.length - 1);
        // Gruppera list efter infoKey(ord)
        var groups = {};
        var order = [];
        list.forEach(function(e){
            var k = infoKey(e, ord);
            if(!(k in groups)){ groups[k] = []; order.push(k); }
            groups[k].push(e);
        });
        return order.map(function(k){
            var label = k || (ord === 1 ? '(Övrigt)' : '');
            if(ord === 1){
                // Yttersta: bara caps-rubrik utanför boxar
                var floorHead = k ? '<div style="font-size:13px;font-weight:800;color:#024550;text-transform:uppercase;letter-spacing:.5px;padding:14px 16px 6px">' + _calcSummaryEsc(label) + '</div>' : '';
                return floorHead + renderLevel(groups[k], levelIdx + 1);
            }
            // Inre nivå (ordning 2+): wrappa i rum-box
            var inner = renderLevel(groups[k], levelIdx + 1);
            var head = k ? '<div style="font-size:14px;font-weight:700;color:#1a1a1a;padding:12px 16px 8px;border-bottom:1px solid #e5e7eb">' + _calcSummaryEsc(label) + '</div>' : '';
            return '<div style="background:#f8fafc;border:1px solid #e5e7eb;border-radius:12px;margin:0 8px 10px;overflow:hidden">'
                + head
                + '<div style="padding:4px 8px">' + inner + '</div>'
                + '</div>';
        }).join('');
    }
    return renderLevel(entries, 0);
}

function _calcSummaryRenderEntry(cat, e){
    // Produktraderna visas ALLTID (namn + Våning/Rum). Penna/kryss visas ALLTID.
    // Ögat döljer BARA priset och expand-sektionen med tillval-detaljer.
    var catOpen = !!_calcSummaryCatExpanded[cat];
    var tillval = (Array.isArray(e.tillval) ? e.tillval : []).filter(function(t){ return t && !t.hidden; });
    var expandSection = '';
    if(catOpen){
        var rows = '';
        if(e.variant_label){
            rows += '<div class="calc-summary-expand-row"><span>Variant: <strong style="color:#1a1a1a">' + _calcSummaryEsc(e.variant_label) + '</strong></span><span></span></div>';
        }
        tillval.forEach(function(t){
            var qtyPart = (t.qty && t.qty !== 1) ? ' × ' + t.qty : '';
            var valuePart = t.value ? ': <strong style="color:#1a1a1a">' + _calcSummaryEsc(t.value) + '</strong>' : '';
            var nameLabel = _calcSummaryEsc(t.name || '') + valuePart + qtyPart;
            rows += '<div class="calc-summary-expand-row"><span>' + nameLabel + '</span><span></span></div>';
        });
        if(rows) expandSection = '<div class="calc-summary-expand">' + rows + '</div>';
    }
    // Våning + Rum som subtitle (så användaren ser vilket rum fönstret tillhör även utan öga)
    var _roomParts = [];
    tillval.forEach(function(t){
        if(!t || !t.value) return;
        var id = String(t.id || '').toLowerCase();
        var nm = String(t.name || '').toLowerCase();
        if(id === 'våning' || id === 'vaning' || nm === 'våning') _roomParts.push('Våning: ' + t.value);
        else if(id === 'rum' || nm === 'rum') _roomParts.push('Rum: ' + t.value);
    });
    var roomSub = _roomParts.length ? '<div style="font-size:11px;color:#64748b;margin-top:2px">' + _calcSummaryEsc(_roomParts.join(' · ')) + '</div>' : '';

    var gross = parseFloat(e.subtotal) || ((parseFloat(e.total) || 0) + (parseFloat(e.deduction_amount) || 0));
    var priceCell = catOpen
        ? '<div class="calc-summary-row-price">' + gross.toLocaleString('sv-SE') + ' kr</div>'
        : '<div class="calc-summary-row-price"></div>';
    // Penna + kryss visas ALLTID. Ögat styr bara priset.
    var actionBtns = '<button class="calc-summary-edit" onclick="calcSummaryEdit(\'' + _calcSummaryEsc(cat) + '\',\'' + _calcSummaryEsc(e.entry_id) + '\', this)" title="Redigera">'
        +   '<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" fill="none" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>'
        + '</button>'
        + '<button class="calc-summary-del" onclick="calcSummaryRemove(\'' + _calcSummaryEsc(cat) + '\',\'' + _calcSummaryEsc(e.entry_id) + '\')" title="Ta bort">×</button>';
    return '<div class="calc-summary-row">'
        + '<div class="calc-summary-row-main">'
        +   '<div class="calc-summary-row-name">' + ((parseInt(e.qty)||1) > 1 ? (parseInt(e.qty) + '× ') : '') + _calcSummaryEsc(e.description || e.product_name || cat) + '</div>'
        +   '<div class="calc-summary-row-cat">' + _calcSummaryEsc(_calcSummaryCatLabel(cat)) + roomSub + '</div>'
        + '</div>'
        + priceCell
        + actionBtns
        + expandSection
        + '</div>';
}

function renderCalcSummary(){
    var content = document.getElementById('calcSummaryContent');
    var sessEl = document.getElementById('calcSummarySession');
    if(!content) return;

    var sid = (typeof prisGetCurrentSessionId === 'function') ? prisGetCurrentSessionId() : '(saknas)';
    if(sessEl){
        sessEl.innerHTML = '<span class="calc-summary-sid-pill">SESSION: ' + _calcSummaryEsc(sid) + '</span>';
    }

    var _addBtnHtml = '<div class="calc-summary-add-row">'
        + '<button class="calc-summary-add-btn" onclick="calcSummaryAddProduct()">'
        +   '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>'
        +   '<span>Lägg till produkt</span>'
        + '</button>'
        + '</div>';

    if(typeof _kalkylItems === 'undefined' || !_kalkylItems || !Object.keys(_kalkylItems).length){
        content.innerHTML = '<div class="calc-summary-empty">Inga produkter i kalkylen ännu. Lägg till via Ny Kalkyl.</div>' + _addBtnHtml;
        _renderCalcSummarySidebar();
        return;
    }

    var html = '';
    var catOrder = ['solceller','batteri','laddbox','varmepump','taktvatt','tak','fonster','isolering','_addons'];
    var seen = {};
    var orderedKeys = [];
    // Interna state-nycklar (start med _ och slutar på State) är inte kategorier
    function _isMeta(k){ return k === '_addonsState' || (k.charAt(0) === '_' && /State$/.test(k)); }
    catOrder.forEach(function(k){ if(_kalkylItems[k] && !_isMeta(k)){ orderedKeys.push(k); seen[k] = true; } });
    Object.keys(_kalkylItems).forEach(function(k){ if(!seen[k] && !_isMeta(k)) orderedKeys.push(k); });

    orderedKeys.forEach(function(cat){
        var item = _kalkylItems[cat];
        if(!item) return;
        var entriesAll = Array.isArray(item.entries) ? item.entries : [item];
        // Filtrera bort dolda addon-rader i UI (men de finns kvar i _kalkylItems för totalt)
        var entries = entriesAll.filter(function(e){ return e && !e.hidden; });
        if(!entries.length) return;
        var catTotal = 0;
        entries.forEach(function(e){
            var net = parseFloat(e.total) || 0;
            var ded = parseFloat(e.deduction_amount) || 0;
            var sub = parseFloat(e.subtotal) || (net + ded);
            catTotal += sub;
        });
        var catOpen = !!_calcSummaryCatExpanded[cat];
        var catEyeIcon = catOpen
            ? '<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" fill="none" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>'
            : '<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" fill="none" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
        var catAddonsHtml = '';
        if(catOpen && cat !== '_addons' && typeof _kalkylItems !== 'undefined' && _kalkylItems['_addons']){
            var addonEntries = Array.isArray(_kalkylItems['_addons'].entries) ? _kalkylItems['_addons'].entries : [];
            var fromCat = addonEntries.filter(function(a){ return a && a._addon_from_cat === cat; });
            if(fromCat.length){
                catAddonsHtml = '<div style="margin-top:8px;padding:10px 14px;background:#fef9e8;border:1px solid #fde68a;border-radius:8px">'
                    + '<div style="font-size:11px;font-weight:700;color:#92400e;text-transform:uppercase;letter-spacing:.4px;margin-bottom:6px">Kategori-add-ons</div>';
                fromCat.forEach(function(a){
                    var line = '<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 0;font-size:13px">';
                    line += '<span style="color:#1a1a1a">' + _calcSummaryEsc(a.description || a.product_name || a.product_id);
                    if(a._addon_unique) line += ' <span style="font-size:10px;color:#059669;font-weight:700;text-transform:uppercase;margin-left:4px">unik</span>';
                    if(a.hidden) line += ' <span style="font-size:10px;color:#dc2626;font-weight:700;text-transform:uppercase;margin-left:4px">dold</span>';
                    line += '</span>';
                    line += '<span style="font-weight:700;color:#024550">' + (parseFloat(a.subtotal||a.total||0)).toLocaleString('sv-SE') + ' kr</span>';
                    line += '</div>';
                    catAddonsHtml += line;
                });
                catAddonsHtml += '</div>';
            }
        }
        html += '<div class="calc-summary-group">'
            + '<div class="calc-summary-group-header" style="display:flex;align-items:center;gap:8px">'
            +   '<button onclick="toggleCalcSummaryCat(\'' + _calcSummaryEsc(cat) + '\')" title="Visa/dölj allt" style="background:none;border:none;padding:4px;cursor:pointer;color:#64748b;display:flex;align-items:center">' + catEyeIcon + '</button>'
            +   '<span style="flex:1">' + _calcSummaryEsc(_calcSummaryCatLabel(cat)) + '</span>'
            +   '<span>' + catTotal.toLocaleString('sv-SE') + ' kr</span>'
            + '</div>'
            + _calcSummaryRenderGrouped(cat, entries)
            + catAddonsHtml
            + '</div>';
    });

    // "Lägg till produkt"-knapp längst ner — går tillbaka till kategori-valet
    html += _addBtnHtml;

    content.innerHTML = html;
    _renderCalcSummarySidebar();
}

function calcSummaryAddProduct(){
    // Stäng Calc_summary och visa CalcBuilder-grid (samma som "Ny Kalkyl" men utan att rensa varukorgen)
    var page = document.getElementById('page-calc-summary');
    if(page){ page.classList.remove('active'); page.style.display = 'none'; }

    if(typeof navigateTo === 'function'){
        navigateTo('konfigurator');
    } else {
        var kp = document.getElementById('page-konfigurator');
        if(kp){ kp.classList.add('active'); kp.style.display = 'block'; }
    }

    var listView = document.getElementById('kalkylListView');
    var cfgView  = document.getElementById('kalkylConfigView');
    if(listView) listView.style.display = 'none';
    if(cfgView){ cfgView.style.display = 'block'; }

    // Städa undan aktiv konfigurator + gamla gridden
    var renderArea = document.getElementById('newCalcRenderArea');
    if(renderArea) renderArea.remove();
    ['affarView','solarConfigView','genericConfigView','fonsterConfigView','batteriConfigView','laddboxConfigView','taktvatConfigView','varmepumpConfigView','takConfigView','isoleringConfigView','cfgCategoryGrid'].forEach(function(id){
        var el = document.getElementById(id);
        if(el){ el.style.display = 'none'; el.classList.remove('active'); }
    });

    // Nollställ aktiv kategori-val men BEHÅLL _kalkylItems (varukorgen)
    if(typeof _newCalcContext !== 'undefined'){
        _newCalcContext.selectedProductId = null;
        _newCalcContext.tillval = [];
        _newCalcContext.categoryId = '';
    }

    // Visa CalcBuilder-griden (newCalcShell + newCalcGrid med alla kategori-kort)
    if(typeof window._showNewCalcGrid === 'function') window._showNewCalcGrid();
    if(typeof window._loadNewCalcCards === 'function') window._loadNewCalcCards();
    try { window._newCalcActive = true; } catch(e){}

    try { window.scrollTo(0, 0); } catch(e){}
}

function _renderCalcSummarySidebar(){
    var el = document.getElementById('calcSummaryPrisSidebar');
    var renderer = (typeof renderPriceSummary === 'function') ? renderPriceSummary
                 : (typeof renderPrisSidebar === 'function') ? renderPrisSidebar : null;
    if(!el || !renderer) return;

    // Aggregera GROSS subtotal + POTENTIELLT avdrag PER tax_type
    var subtotal = 0;
    var breakdown = { ROT: 0, GT20: 0, GT50: 0 };
    var aggTaxType = 'NONE';
    if(typeof _kalkylItems !== 'undefined' && _kalkylItems){
        Object.keys(_kalkylItems).forEach(function(k){
            if(k === '_addonsState' || (k.charAt(0) === '_' && /State$/.test(k))) return;
            var item = _kalkylItems[k];
            if(!item) return;
            var entries = Array.isArray(item.entries) ? item.entries : [item];
            entries.forEach(function(e){
                var gross = parseFloat(e.subtotal || 0) || parseFloat(e.total || 0);
                subtotal += gross;
                var ded = parseFloat(e.deduction_amount) || 0;
                var tt  = e.tax_type || 'NONE';
                if(ded > 0 && breakdown.hasOwnProperty(tt)) breakdown[tt] += ded;
                if(aggTaxType === 'NONE' && tt !== 'NONE') aggTaxType = tt;
            });
        });
    }
    var potentialDed = breakdown.ROT + breakdown.GT20 + breakdown.GT50;

    // Effektivt avdrag = min(potentiellt, owners × maxPerPerson, slider)
    var deductType = _calcSummaryState.deductType;
    if(!deductType){
        deductType = potentialDed > 0 ? ((aggTaxType === 'ROT') ? 'rot' : 'green') : 'none';
    }
    var owners = _calcSummaryState.owners || 1;
    var tax = (typeof taxSettings !== 'undefined') ? taxSettings : { rotMax: 50000, gt50: 50, gt20: 20, rot: 30, moms: 25 };
    var maxPerPerson = (aggTaxType === 'ROT') ? (tax.rotMax || 50000) : 50000;
    var ownerCap = owners * maxPerPerson;
    var sliderMax = ownerCap;
    var sliderValue = (_calcSummaryState.sliderValue != null) ? Math.min(parseFloat(_calcSummaryState.sliderValue), sliderMax) : sliderMax;
    var effectiveDed = (deductType === 'none') ? 0 : Math.min(potentialDed, sliderValue);

    renderer('calcSummaryPrisSidebar', {
        lines: [],
        subtotal: subtotal,
        totalIsAll: true,
        taxType: aggTaxType,
        taxBreakdown: breakdown,        // {ROT, GT20, GT50} — per-typ potentiella avdrag
        deductType: deductType,
        deductAmount: effectiveDed,
        owners: owners,
        sliderInteractive: true,
        sliderValue: sliderValue,
        maxDeduction: sliderMax,
        total: subtotal - effectiveDed,
        finYears: _calcSummaryState.finYears || 15,
        monthly: 0,
        category: '_summary',
        onDeductChange: 'calcSummarySetDeductType',
        onOwnerChange: 'calcSummarySetOwners',
        onFinChange: 'calcSummarySetFinYears',
        onSliderChange: 'calcSummarySetSliderValue'
    });
}

function calcSummarySetOwners(n){ _calcSummaryState.owners = parseInt(n) || 1; _calcSummaryState.sliderValue = null; _renderCalcSummarySidebar(); }
function calcSummarySetDeductType(t){ _calcSummaryState.deductType = t || 'none'; _renderCalcSummarySidebar(); }
function calcSummarySetSliderValue(v){ _calcSummaryState.sliderValue = parseFloat(v) || 0; _renderCalcSummarySidebar(); }
function calcSummarySetFinYears(y){ _calcSummaryState.finYears = parseInt(y) || 0; _renderCalcSummarySidebar(); }

function calcSummaryEdit(cat, entryId, btnEl){
    if(typeof editSummaryEntry !== 'function') return;
    // Gråa ut knappen direkt så användaren inte klickar igen
    if(btnEl){
        btnEl.disabled = true;
        btnEl.style.opacity = '.35';
        btnEl.style.cursor = 'wait';
        btnEl.style.pointerEvents = 'none';
    }
    closeCalcSummary();
    // editSummaryEntry returnerar Promise som resolvar när allt laddat + openForEdit körd
    setTimeout(function(){
        var p = editSummaryEntry(cat, entryId, btnEl);
        // Fallback-reset (om editSummaryEntry inte hanterar det)
        if(p && typeof p.then === 'function'){
            p.then(function(){
                if(btnEl){ btnEl.disabled=false; btnEl.style.opacity=''; btnEl.style.cursor=''; btnEl.style.pointerEvents=''; }
            });
        }
    }, 100);
}

function calcSummaryRemove(cat, entryId){
    if(!confirm('Ta bort den här raden från kalkylen?')) return;
    // Addons (_addons) måste tas bort permanent via disableAddon, annars
    // lägger _prisRecalculateAddons tillbaka dem direkt.
    if(cat === '_addons' && typeof disableAddon === 'function'){
        disableAddon(entryId);
        renderCalcSummary();
        return;
    }
    if(typeof removeKalkylEntry === 'function'){
        removeKalkylEntry(cat, entryId);
    }
    renderCalcSummary();
}

// ============================================================
// HEADER: Kundinfo (expand/edit/save) + prospektbilder + actions
// ============================================================
var _calcSummaryQuote = { id:null, quote_number:null, sent_date:null, customer_name:'', customer_address:'', customer_email:'', customer_phone:'', customer_personnummer:'', notes:'', status:'utkast', images:[] };
var _calcSummaryCustomerOpen = false;
var _calcSummaryImagesOpen = false;
var _calcSummarySaveTimer = null;

function _calcSummaryLoadQuote(){
    var id = (typeof currentQuoteId !== 'undefined' && currentQuoteId) ? currentQuoteId : null;
    _calcSummaryQuote.id = id;
    if(!id){
        _calcSummaryQuote.quote_number = null;
        _calcSummaryQuote.sent_date = null;
        _calcSummaryQuote.customer_name = '';
        _calcSummaryQuote.customer_address = '';
        _calcSummaryQuote.customer_email = '';
        _calcSummaryQuote.customer_phone = '';
        _calcSummaryQuote.customer_personnummer = '';
        _calcSummaryQuote.notes = '';
        _calcSummaryQuote.status = 'utkast';
        _calcSummaryQuote.images = [];
        return;
    }
    fetch('/api/quotes.php?id=' + encodeURIComponent(id))
        .then(function(r){ return r.json(); })
        .then(function(d){
            var q = (d && d.quote) ? d.quote : (d && d.success && d.quotes && d.quotes[0]) ? d.quotes[0] : null;
            if(!q) return;
            _calcSummaryQuote.quote_number = q.quote_number || null;
            _calcSummaryQuote.sent_date = q.sent_date || null;
            _calcSummaryQuote.customer_name = q.customer_name || '';
            _calcSummaryQuote.customer_address = q.customer_address || '';
            _calcSummaryQuote.customer_email = q.customer_email || '';
            _calcSummaryQuote.customer_phone = q.customer_phone || '';
            _calcSummaryQuote.customer_personnummer = q.customer_personnummer || '';
            _calcSummaryQuote.notes = q.notes || '';
            _calcSummaryQuote.status = q.status || 'utkast';
            var imgs = q.images;
            if(typeof imgs === 'string'){ try{ imgs = JSON.parse(imgs); }catch(e){ imgs = []; } }
            _calcSummaryQuote.images = Array.isArray(imgs) ? imgs : [];
            _calcSummaryRenderHeader();
        })
        .catch(function(e){ console.warn('[CalcSummary] loadQuote failed', e); });
}

function _calcSummaryStatusLabel(s){
    return ({utkast:'Utkast',offert:'Offert',skickad:'Skickad',accepterad:'Godkänd',avslutad:'Avslutad'})[s] || s || 'Utkast';
}
function _calcSummaryStatusColor(s){
    return ({utkast:'#64748b',offert:'#0284c7',skickad:'#d97706',accepterad:'#059669',avslutad:'#4b5563'})[s] || '#64748b';
}

function _calcSummaryRenderHeader(){
    var host = document.getElementById('calcSummaryHeader');
    if(!host) return;
    var q = _calcSummaryQuote;
    var hasName = !!(q.customer_name || '').trim();
    var custSummary = hasName
        ? _calcSummaryEsc(q.customer_name) + (q.customer_address ? ' — ' + _calcSummaryEsc(q.customer_address) : '')
        : '<span style="color:#94a3b8;font-weight:500">Ej ifylld</span>';
    var imgCount = (q.images || []).length;
    var imgSummary = imgCount ? (imgCount + ' bild' + (imgCount>1?'er':'')) : '<span style="color:#94a3b8;font-weight:500">Inga bilder</span>';

    var statusLbl = _calcSummaryStatusLabel(q.status);
    var statusCol = _calcSummaryStatusColor(q.status);

    var html = '';

    // Kundinformation-rad
    html += '<div class="cs-head-block">'
        + '<div class="cs-head-row" onclick="calcSummaryToggleCustomer()">'
        +   '<div class="cs-head-left">'
        +     '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>'
        +     '<strong>Kundinformation</strong>'
        +     '<span class="cs-head-summary">' + custSummary + '</span>'
        +   '</div>'
        +   '<svg class="cs-head-chev" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2.5" style="transform:rotate(' + (_calcSummaryCustomerOpen?'0':'-90') + 'deg)"><polyline points="6 9 12 15 18 9"/></svg>'
        + '</div>'
        + '<div class="cs-head-body" style="' + (_calcSummaryCustomerOpen?'':'display:none') + '">'
        +   _calcSummaryCustomerForm()
        + '</div>'
        + '</div>';

    // Prospektbilder-rad
    html += '<div class="cs-head-block">'
        + '<div class="cs-head-row" onclick="calcSummaryToggleImages()">'
        +   '<div class="cs-head-left">'
        +     '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>'
        +     '<strong>Prospektbilder</strong>'
        +     '<span class="cs-head-summary">' + imgSummary + '</span>'
        +   '</div>'
        +   '<svg class="cs-head-chev" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2.5" style="transform:rotate(' + (_calcSummaryImagesOpen?'0':'-90') + 'deg)"><polyline points="6 9 12 15 18 9"/></svg>'
        + '</div>'
        + '<div class="cs-head-body" style="' + (_calcSummaryImagesOpen?'':'display:none') + '">'
        +   _calcSummaryImagesPanel()
        + '</div>'
        + '</div>';

    // Status + actions
    var hasOffert = !!q.quote_number;
    var isSent = q.status === 'skickad';
    var primaryLabel = hasOffert ? 'Visa offert (PDF)' : 'Skapa offert (PDF)';
    var sentTxt = '';
    if(isSent && q.sent_date){
        var d = String(q.sent_date).replace('T',' ').substring(0,16);
        sentTxt = '<span class="cs-sent-date">Skickad ' + _calcSummaryEsc(d) + '</span>';
    }
    html += '<div class="cs-actions-row">'
        + '<span class="cs-status-badge" style="background:' + statusCol + '">' + _calcSummaryEsc(statusLbl) + '</span>'
        + '<button class="cs-action-btn cs-action-primary" onclick="calcSummaryCreateOffer()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg> ' + primaryLabel + '</button>'
        + '<button class="cs-action-btn" onclick="calcSummarySendOffer()"' + (!hasOffert?' disabled':'') + '><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2 11 13"/><path d="m22 2-7 20-4-9-9-4 20-7Z"/></svg> Skicka</button>'
        + '<button class="cs-action-btn" onclick="calcSummaryPrint()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg> Skriv ut</button>'
        + sentTxt
        + (hasOffert ? '<span class="cs-quote-id">Offert-ID #' + _calcSummaryEsc(q.quote_number) + '</span>' : '')
        + '</div>';

    host.innerHTML = html;
}

function _calcSummaryCustomerForm(){
    var q = _calcSummaryQuote;
    return '<div class="cs-form">'
        + _csField('Namn','customer_name',q.customer_name,'text')
        + _csField('Adress','customer_address',q.customer_address,'text')
        + _csField('Email','customer_email',q.customer_email,'email')
        + _csField('Telefon','customer_phone',q.customer_phone,'tel')
        + _csField('Personnummer','customer_personnummer',q.customer_personnummer,'text')
        + '<div class="cs-field cs-field-wide"><label>Anteckningar</label>'
        +   '<textarea rows="3" oninput="calcSummaryPatch(\'notes\',this.value)">' + _calcSummaryEsc(q.notes) + '</textarea>'
        + '</div>'
        + '<div class="cs-field cs-field-wide" style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">'
        +   '<button id="csSaveBtn" onclick="calcSummarySaveCustomer()" style="padding:10px 22px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:700;cursor:pointer;font-family:inherit">Spara kunduppgifter</button>'
        + '</div>'
        + '</div>';
}

function calcSummarySaveCustomer(){
    // Force-flush eventuell pending debounce och spara nu
    if(_calcSummarySaveTimer){ clearTimeout(_calcSummarySaveTimer); _calcSummarySaveTimer = null; }
    var btn = document.getElementById('csSaveBtn');
    if(btn){ btn.disabled = true; btn.textContent = 'Sparar...'; }
    return _calcSummaryPersistNow().then(function(ok){
        if(btn){
            btn.disabled = false;
            btn.textContent = ok ? 'Sparat ✓' : 'Försök igen';
            btn.style.background = ok ? '#10b981' : '#dc2626';
            setTimeout(function(){
                if(!btn) return;
                btn.textContent = 'Spara kunduppgifter';
                btn.style.background = '#024550';
            }, 1800);
        }
    });
}

function _calcSummaryPersistNow(){
    // Synkron-aktig save som returnerar Promise<boolean>
    var id = _calcSummaryQuote.id || (typeof currentQuoteId !== 'undefined' ? currentQuoteId : null);
    if(!id){
        alert('Spara kalkylen först (klicka + Lägg till produkt eller Spara).');
        return Promise.resolve(false);
    }
    var keys = ['customer_name','customer_address','customer_email','customer_phone','customer_personnummer','notes'];
    var body = { id: id };
    keys.forEach(function(k){ body[k] = _calcSummaryQuote[k]; });
    return fetch('/api/quotes.php', {
        method:'POST',
        headers:{'Content-Type':'application/json'},
        body: JSON.stringify(body)
    }).then(function(r){ return r.json(); }).then(function(d){
        return !!(d && d.success);
    }).catch(function(e){ console.warn('[CalcSummary] save error', e); return false; });
}
window.calcSummarySaveCustomer = calcSummarySaveCustomer;

function _csField(label,key,val,type){
    return '<div class="cs-field"><label>' + _calcSummaryEsc(label) + '</label>'
        + '<input type="' + type + '" value="' + _calcSummaryEsc(val) + '" oninput="calcSummaryPatch(\'' + key + '\',this.value)">'
        + '</div>';
}

function _calcSummaryImagesPanel(){
    var imgs = _calcSummaryQuote.images || [];
    var html = '<div class="cs-img-grid">';
    imgs.forEach(function(im, idx){
        var url = (typeof im === 'string') ? im : (im && im.url ? im.url : '');
        if(!url) return;
        html += '<div class="cs-img-thumb">'
            + '<img src="' + _calcSummaryEsc(url) + '" alt="">'
            + '<button class="cs-img-del" onclick="calcSummaryRemoveImage(' + idx + ')" title="Ta bort">×</button>'
            + '</div>';
    });
    html += '<label class="cs-img-add"><input type="file" accept="image/*" multiple style="display:none" onchange="calcSummaryUploadImages(this.files)"><span>+ Ladda upp</span></label>';
    html += '</div>';
    return html;
}

function calcSummaryToggleCustomer(){ _calcSummaryCustomerOpen = !_calcSummaryCustomerOpen; _calcSummaryRenderHeader(); }
function calcSummaryToggleImages(){ _calcSummaryImagesOpen = !_calcSummaryImagesOpen; _calcSummaryRenderHeader(); }

function calcSummaryPatch(key, val){
    _calcSummaryQuote[key] = val;
    if(_calcSummarySaveTimer) clearTimeout(_calcSummarySaveTimer);
    _calcSummarySaveTimer = setTimeout(function(){ _calcSummaryPersist([key]); }, 700);
}

function _calcSummaryPersist(keys){
    var id = _calcSummaryQuote.id || (typeof currentQuoteId !== 'undefined' ? currentQuoteId : null);
    if(!id){
        // Ingen sparad kalkyl än — kräv save först via existing flow
        if(typeof saveCfgQuote === 'function'){
            saveCfgQuote(_calcSummaryQuote.category || 'solceller');
            setTimeout(function(){ _calcSummaryPersist(keys); }, 400);
        }
        return;
    }
    _calcSummaryQuote.id = id;
    var body = { id: id };
    (keys || ['customer_name','customer_address','customer_email','customer_phone','customer_personnummer','notes','status','images']).forEach(function(k){ body[k] = _calcSummaryQuote[k]; });
    fetch('/api/quotes.php', {
        method:'POST',
        headers:{'Content-Type':'application/json'},
        body: JSON.stringify(body)
    }).then(function(r){ return r.json(); })
    .then(function(d){
        if(!d || !d.success) console.warn('[CalcSummary] save failed', d);
        // Re-render BARA när formulär är stängt — annars tappar input fokus mitt i skrivning.
        if(!_calcSummaryCustomerOpen && !_calcSummaryImagesOpen) _calcSummaryRenderHeader();
    })
    .catch(function(e){ console.warn('[CalcSummary] save error', e); });
}

function calcSummaryUploadImages(files){
    if(!files || !files.length) return;
    var id = _calcSummaryQuote.id || (typeof currentQuoteId !== 'undefined' ? currentQuoteId : null);
    if(!id){ alert('Spara kalkylen först innan du laddar upp bilder.'); return; }
    var fd = new FormData();
    fd.append('quote_id', id);
    for(var i=0;i<files.length;i++) fd.append('images[]', files[i]);
    fetch('/api/quote_images.php', { method:'POST', body: fd })
        .then(function(r){ return r.json(); })
        .then(function(d){
            if(d && d.success && Array.isArray(d.urls)){
                d.urls.forEach(function(u){ _calcSummaryQuote.images.push(u); });
                _calcSummaryPersist(['images']);
                _calcSummaryRenderHeader();
            } else {
                alert('Uppladdning misslyckades: ' + ((d && d.error) || 'okänt fel'));
            }
        })
        .catch(function(e){ alert('Fel vid uppladdning: ' + e); });
}

function calcSummaryRemoveImage(idx){
    if(!confirm('Ta bort bilden?')) return;
    _calcSummaryQuote.images.splice(idx, 1);
    _calcSummaryPersist(['images']);
    _calcSummaryRenderHeader();
}

function calcSummaryCreateOffer(){
    var id = _calcSummaryQuote.id || (typeof currentQuoteId !== 'undefined' ? currentQuoteId : null);
    if(!id){ alert('Spara kalkylen först.'); return; }
    // Om offert redan finns (har quote_number) → bara öppna PDF
    if(_calcSummaryQuote.quote_number){ _openQuotePdfModal(id); return; }
    // Annars: skapa offert (status → offert, backend genererar quote_number) och öppna PDF efter svar
    _calcSummaryQuote.status = 'offert';
    fetch('/api/quotes.php', {
        method:'POST',
        headers:{'Content-Type':'application/json'},
        body: JSON.stringify({ id: id, status: 'offert' })
    }).then(function(r){ return r.json(); })
    .then(function(d){
        if(d && d.quote_number) _calcSummaryQuote.quote_number = d.quote_number;
        _calcSummaryRenderHeader();
        _openQuotePdfModal(id);
    })
    .catch(function(e){ console.warn('[CalcSummary] create offer failed', e); _openQuotePdfModal(id); });
}

function _openQuotePdfModal(id){
    // Ta bort eventuell existerande modal
    var existing = document.getElementById('csPdfModal');
    if(existing) existing.remove();

    var url = '/api/quote_pdf.php?id=' + id + '&modal=1&t=' + Date.now();
    var modal = document.createElement('div');
    modal.id = 'csPdfModal';
    modal.style.cssText = 'position:fixed;inset:0;z-index:10000;background:rgba(15,23,42,.75);display:flex;align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(4px)';
    modal.innerHTML =
        '<div style="position:relative;width:min(960px,95vw);height:90vh;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.35);display:flex;flex-direction:column">'
      +   '<div style="display:flex;justify-content:space-between;align-items:center;padding:12px 18px;border-bottom:1px solid #e5e7eb;background:#f8fafc">'
      +     '<strong style="font-size:14px;color:#024550">Offert #' + id + '</strong>'
      +     '<div style="display:flex;gap:8px">'
      +       '<a href="' + url + '" target="_blank" rel="noreferrer" style="padding:6px 12px;background:#fff;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-weight:600;color:#024550;text-decoration:none">Öppna i ny flik</a>'
      +       '<button onclick="_printQuoteIframe()" style="padding:6px 12px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">Skriv ut</button>'
      +       '<button onclick="_closeQuotePdfModal()" style="padding:6px 12px;background:#fff;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">Stäng</button>'
      +     '</div>'
      +   '</div>'
      +   '<iframe id="csPdfIframe" src="' + url + '" style="flex:1;width:100%;border:none;background:#fff"></iframe>'
      + '</div>';
    modal.onclick = function(e){ if(e.target === modal) _closeQuotePdfModal(); };
    document.body.appendChild(modal);
    document.body.style.overflow = 'hidden';
}

function _closeQuotePdfModal(){
    var m = document.getElementById('csPdfModal');
    if(m) m.remove();
    document.body.style.overflow = '';
}

function _printQuoteIframe(){
    var iframe = document.getElementById('csPdfIframe');
    if(!iframe) return;
    try {
        iframe.contentWindow.focus();
        iframe.contentWindow.print();
    } catch(e){ alert('Kunde inte skriva ut: ' + e.message); }
}

function calcSummarySendOffer(){
    var id = _calcSummaryQuote.id || (typeof currentQuoteId !== 'undefined' ? currentQuoteId : null);
    if(!id){ alert('Spara kalkylen först.'); return; }
    var email = (_calcSummaryQuote.customer_email || '').trim();
    if(!email){ alert('Fyll i kundens email först.'); return; }
    if(!confirm('Skicka offerten till ' + email + '?')) return;
    fetch('/api/quote_send.php', {
        method:'POST',
        headers:{'Content-Type':'application/json'},
        body: JSON.stringify({ id: id })
    }).then(function(r){ return r.json(); })
    .then(function(d){
        if(d && d.success){
            _calcSummaryQuote.status = 'skickad';
            // Backend sätter sent_date=NOW(); reflektera lokalt så headern visar direkt
            var now = new Date();
            var pad = function(n){ return n<10 ? '0'+n : ''+n; };
            _calcSummaryQuote.sent_date = now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate())+' '+pad(now.getHours())+':'+pad(now.getMinutes());
            _calcSummaryRenderHeader();
            alert('Offert skickad till ' + email);
        } else {
            alert('Kunde inte skicka: ' + ((d && d.error) || 'okänt fel'));
        }
    });
}

function calcSummaryPrint(){ window.print(); }

window.calcSummaryToggleCustomer = calcSummaryToggleCustomer;
window.calcSummaryToggleImages = calcSummaryToggleImages;
window.calcSummaryPatch = calcSummaryPatch;
window.calcSummaryUploadImages = calcSummaryUploadImages;
window.calcSummaryRemoveImage = calcSummaryRemoveImage;
window.calcSummaryCreateOffer = calcSummaryCreateOffer;
window.calcSummarySendOffer = calcSummarySendOffer;
window._openQuotePdfModal = _openQuotePdfModal;
window._closeQuotePdfModal = _closeQuotePdfModal;
window._printQuoteIframe = _printQuoteIframe;
window.calcSummaryAddProduct = calcSummaryAddProduct;
window.calcSummaryPrint = calcSummaryPrint;

window.openCalcSummary = openCalcSummary;
window.closeCalcSummary = closeCalcSummary;
window.renderCalcSummary = renderCalcSummary;
window.toggleCalcSummaryRow = toggleCalcSummaryRow;
window.calcSummaryEdit = calcSummaryEdit;
window.calcSummaryRemove = calcSummaryRemove;
window.calcSummarySetOwners = calcSummarySetOwners;
window.calcSummarySetDeductType = calcSummarySetDeductType;
window.calcSummarySetSliderValue = calcSummarySetSliderValue;
window.calcSummarySetFinYears = calcSummarySetFinYears;