backups/product-editor-clickable-bak.js

Code: DEV-DF17F709 Size: 96.2 KB Lines: 1456 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/backups/product-editor-clickable-bak.js

Task / Comment

Open report form
// produkter-editor.js - Product edit modal

function editProduct(id) {
    const p = catalogProducts.find(x => x.id === id);
    if(!p) return;
    const cats = [{v:'solceller',l:'Solceller'},{v:'batteri',l:'Batteri'},{v:'batteri_utbyggnad',l:'Batteri Utbyggnad'},{v:'laddbox',l:'Laddbox'},{v:'fonster',l:'Fönster'},{v:'dorrar',l:'Dörrar'},{v:'tak',l:'Tak'},{v:'varmepump',l:'Värmepump'},{v:'taktvatt',l:'Taktvätt'},{v:'isolering',l:'Isolering'},{v:'tjanster',l:'Tjänster'},{v:'tillbehor',l:'Tillbehör'}];
    const stocks = [{v:'I lager',c:'green'},{v:'Få kvar',c:'yellow'},{v:'Beställning',c:'blue'},{v:'Slut',c:'red'}];
    _peVariants = (p.specs && p.specs.variants) ? JSON.parse(JSON.stringify(p.specs.variants)) : [];
    _peSpecsType = (p.specs && p.specs.type) || '';
    _peStyles = (p.specs && p.specs.styles) ? JSON.parse(JSON.stringify(p.specs.styles)) : [];
    _peSizes = (p.specs && p.specs.sizes) ? JSON.parse(JSON.stringify(p.specs.sizes)) : [];
    _peSpecsMeta = p.specs ? JSON.parse(JSON.stringify(p.specs)) : {};
    _peWinModels = (p.specs && p.specs.models) ? JSON.parse(JSON.stringify(p.specs.models)) : [];

    const modal = document.createElement('div');
    modal.id = 'productEditModal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px';
    modal.onclick = e => { if(e.target === modal) modal.remove(); };

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:1520px;width:min(1520px,96vw);max-height:95vh;overflow-y:auto;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        +'<div style="padding:16px 24px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center">'
        +'<h2 style="font-size:18px;font-weight:700;color:#1a1a1a;margin:0">Redigera: '+p.name+'</h2>'
        +'<button onclick="this.closest(\'#productEditModal\').remove()" style="background:none;border:none;cursor:pointer;padding:4px"><svg viewBox="0 0 24 24" style="width:20px;height:20px;stroke:#94a3b8;fill:none;stroke-width:2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>'
        +'</div>'
        +'<div style="padding:10px 24px 0">'
        +'<div style="display:flex;gap:8px;flex-wrap:wrap">'
        +'<a href="/maps/view.php?file=pages/productlist.php" target="_blank" rel="noreferrer" style="display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid #dbe7ef;border-radius:999px;background:#f8fbfd;color:#1f5160;text-decoration:none;font-size:12px;font-weight:700;font-family:inherit">productlist.php <span style="color:#6b8793;font-weight:800">DEV-0DB99B18</span></a>'
        +'<a href="/maps/view.php?file=js/product-editor.js" target="_blank" rel="noreferrer" style="display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid #dbe7ef;border-radius:999px;background:#f8fbfd;color:#1f5160;text-decoration:none;font-size:12px;font-weight:700;font-family:inherit">product-editor.js <span style="color:#6b8793;font-weight:800">DEV-E7734603</span></a>'
        +'<a href="/maps/view.php?file=css/productlist.css" target="_blank" rel="noreferrer" style="display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid #dbe7ef;border-radius:999px;background:#f8fbfd;color:#1f5160;text-decoration:none;font-size:12px;font-weight:700;font-family:inherit">productlist.css <span style="color:#6b8793;font-weight:800">DEV-4059252E</span></a>'
        +'</div>'
        +'</div>'
        +'<style>'
        +'#productEditModal input:not([type=checkbox]):not([type=file]),#productEditModal select,#productEditModal textarea{max-width:100%;min-width:0;box-sizing:border-box}'
        +'#productEditModal .pe-layout{padding:20px 24px;display:grid;grid-template-columns:minmax(260px,300px) minmax(0,1fr);gap:18px;align-items:start}'
        +'#productEditModal .pe-left{min-width:0;padding-right:16px;border-right:1px solid #e5e7eb;overflow:hidden}'
        +'#productEditModal .pe-right{min-width:0;overflow:hidden}'
        +'#productEditModal .pe-form-grid{display:grid;grid-template-columns:72px minmax(0,1fr);gap:8px 10px;align-items:center;font-size:13px}'
        +'@media (max-width: 1180px){#productEditModal .pe-layout{grid-template-columns:1fr}#productEditModal .pe-left{padding-right:0;border-right:none;border-bottom:1px solid #e5e7eb;padding-bottom:16px}#productEditModal .pe-right{padding-top:4px}}'
        +'</style>'
        +'<div class="pe-layout">'

        // VÄNSTER: Grundinfo
        +'<div class="pe-left">'
        +'<div style="margin-bottom:16px">'
        +'<div id="peImgPreview" style="width:100%;aspect-ratio:16/9;background:#f8f9fa;border-radius:10px;border:2px dashed #e5e7eb;display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:pointer;margin-bottom:6px" onclick="document.getElementById(\'peFileInput\').click()">'
        +(p.img ? '<img src="'+p.img+'" style="width:100%;height:100%;object-fit:cover">' : '<div style="text-align:center;color:#94a3b8;font-size:12px">Klicka för bild</div>')
        +'</div>'
        +'<input type="file" id="peFileInput" accept="image/*" style="display:none" onchange="previewProductImage(this)">'
        +'<div id="peUploadStatus" style="font-size:11px;color:#94a3b8"></div>'
        +'</div>'
        +'<div class="pe-form-grid">'
        +'<label style="font-weight:600;color:#64748b">ID</label>'
        +'<input id="peId" value="'+p.id+'" readonly style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;background:#f8f9fa;color:#94a3b8;font-family:inherit">'
        +'<label style="font-weight:600;color:#64748b">Namn</label>'
        +'<input id="peName" value="'+p.name.replace(/"/g,'&quot;')+'" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit">'
        +'<label style="font-weight:600;color:#64748b">Kategori</label>'
        +'<select id="peCat" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit">'+cats.map(c=>'<option value="'+c.v+'"'+(c.v===p.cat?' selected':'')+'>'+c.l+'</option>').join('')+'</select>'
        +'<label style="font-weight:600;color:#64748b">Pris (kr)</label>'
        +'<input id="pePrice" type="number" value="'+p.price+'" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit">'
        +'<label style="font-weight:600;color:#64748b">Inköpspris</label>'
        +'<div style="display:flex;gap:4px"><input id="peCostPrice" type="number" value="'+(p.costPrice||'')+'" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit;flex:1"><select id="peCostCurrency" style="padding:7px 6px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;width:70px">'+(function(){var cs=['SEK','EUR','USD','DKK','NOK'];return cs.map(function(c){return '<option value="'+c+'"'+((p.costCurrency||'SEK')===c?' selected':'')+'>'+c+'</option>';}).join('');}())+'</select></div>'
        +'<label style="font-weight:600;color:#64748b">Lager</label>'
        +'<select id="peStock" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit">'+stocks.map(s=>'<option value="'+s.v+'" data-class="'+s.c+'"'+(s.v===p.stock?' selected':'')+'>'+s.v+'</option>').join('')+'</select>'
        +'<label style="font-weight:600;color:#64748b">Leverantör</label>'
        +'<select id="peSupplier" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit"><option value="">– Ingen –</option>'+_suppliers.map(s=>'<option value="'+s.id+'"'+(s.id==p.supplierId?' selected':'')+'>'+s.name+'</option>').join('')+'</select>'
        +'<label style="font-weight:600;color:#64748b">Enhet</label>'
        +'<select id="peUnit" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit">'
        +['st','kvm','m²','löpmeter','panel','mil','kWh','W','paket','tim','m'].map(u=>'<option value="'+u+'"'+(u===p.unit?' selected':'')+'>'+u+'</option>').join('')
        +'</select>'
        +'<label style="font-weight:600;color:#64748b">Påslag</label>'
        +'<div style="display:flex;gap:6px"><select id="peMarkupType" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit;flex:1"><option value="percent"'+(p.markupType==='percent'?' selected':'')+'>Procent (%)</option><option value="amount"'+(p.markupType==='amount'?' selected':'')+'>Belopp (kr)</option></select>'
        +'<input id="peMarkupValue" type="number" step="0.01" value="'+(p.markupValue||0)+'" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit;width:100px" placeholder="0"></div>'
        +'<label style="font-weight:600;color:#64748b">Beskrivning</label>'
        +'<textarea id="peDesc" rows="2" style="padding:7px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:13px;font-family:inherit;resize:vertical">'+(p.desc||'').replace(/</g,'&lt;')+'</textarea>'
        +'</div>'
        +'<div style="margin-top:10px;display:flex;flex-direction:column;gap:6px">'
        +'<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px"><input type="checkbox" id="peGreenTech" '+(p.greenTechEligible?'checked':'')+' style="width:16px;height:16px;accent-color:#059669">Grönt teknik-avdrag</label>'
        +'<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px"><input type="checkbox" id="peRotEligible" '+(p.rotEligible?'checked':'')+' style="width:16px;height:16px;accent-color:#2563eb">ROT-avdrag</label>'
        +'<div style="border-top:1px solid #e5e7eb;padding-top:8px;margin-top:8px">'
        +'<div data-tillval-label style="font-size:11px;font-weight:600;color:#94a3b8;margin-bottom:6px">NÄR PRODUKTEN ANVÄNDS SOM TILLVAL</div>'
        +'<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px"><input type="checkbox" id="peTillvalOblig" '+(p.tillvalObligatorisk?'checked':'')+' style="width:16px;height:16px;accent-color:#f59e0b">Obligatorisk (alltid inkluderad)</label>'
        +'<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;margin-top:4px"><input type="checkbox" id="peTillvalHidden" '+(p.tillvalHidden?'checked':'')+' style="width:16px;height:16px;accent-color:#f59e0b">Dold (syns ej som valbar)</label>'
        +'</div>'
        +'</div>'
        +'<div style="display:flex;gap:8px;margin-top:16px">'
        +'<button onclick="saveProduct(\''+p.id+'\')" style="flex:1;padding:10px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Spara</button>'
        +'<button onclick="deleteProduct(\''+p.id+'\')" style="padding:10px 14px;background:#fee2e2;color:#dc2626;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Ta bort</button>'
        +'</div>'
        +'</div>'

        // HÖGER: Fliksystem
        +'<div class="pe-right">'
        // Flik-navigation
        +'<div style="display:flex;gap:0;border-bottom:2px solid #e5e7eb;margin-bottom:12px">'
        +'<button onclick="peSetTab(\'varianter\')" class="pe-tab" data-tab="varianter" style="padding:8px 16px;font-size:12px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid #024550;color:#024550;margin-bottom:-2px;font-family:inherit">Varianter</button>'
        
        +'<button onclick="peSetTab(\'tjanster\')" class="pe-tab" data-tab="tjanster" style="padding:8px 16px;font-size:12px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;color:#64748b;margin-bottom:-2px;font-family:inherit">Tillval</button>'
        +'<button onclick="peSetTab(\'bilder\')" class="pe-tab" data-tab="bilder" style="padding:8px 16px;font-size:12px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;color:#64748b;margin-bottom:-2px;font-family:inherit">Bilder</button>'
        +'<button onclick="peSetTab(\'spec\')" class="pe-tab" data-tab="spec" style="padding:8px 16px;font-size:12px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;color:#64748b;margin-bottom:-2px;font-family:inherit">Specifikation</button>'
        +'<button onclick="peSetTab(\'dokument\')" class="pe-tab" data-tab="dokument" style="padding:8px 16px;font-size:12px;font-weight:600;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent;color:#64748b;margin-bottom:-2px;font-family:inherit">Dokument</button>'
        +'</div>'

        // Flik: Varianter
        +'<div id="peTabVarianter" class="pe-tab-panel">'
        +'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0;color:#1a1a1a">Varianter</h3>'
        +'<button onclick="peAddVariant()" style="padding:5px 12px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">+ Lägg till</button>'
        +'</div>'
        +'<div id="peVariantsTable" style="max-height:300px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"></div>'
        +'<div style="margin-top:16px;display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0;color:#1a1a1a">Regler <span style="font-weight:400;color:#94a3b8;font-size:11px;cursor:pointer" onclick="document.getElementById(\'peRulesHelp\').style.display=document.getElementById(\'peRulesHelp\').style.display===\'none\'?\'block\':\'none\'">?</span></h3>'
        +'<button onclick="peAddRule()" style="padding:5px 12px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">+ Lägg till</button>'
        +'</div>'
        +'<div id="peRulesHelp" style="display:none;margin-bottom:8px;padding:10px;background:#f0f9ff;border:1px solid #bae6fd;border-radius:8px;font-size:11px;line-height:1.6;color:#334155"><strong style="color:#0284c7">OM</strong> = villkor, <strong style="color:#0284c7">SÅ</strong> = åtgärd, <strong style="color:#0284c7">±</strong> = operator (= sätter, + adderar, − subtraherar, × multiplicerar). Formler: <code>{w}</code> <code>{h}</code> <code>{TB_FODER_width}</code></div>'
        +'<div id="peRulesTable" style="max-height:300px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"></div>'
        +'</div>'
                        // Flik: Tjänster + Tillbehör
        +'<div id="peTabTjanster" class="pe-tab-panel" style="display:none">'
        +'<div style="display:flex;flex-direction:column;gap:12px">'
        +'<div>'
        +'<h3 style="font-size:14px;font-weight:700;margin:0 0 8px;color:#1a1a1a">Kopplade tjänster</h3>'
        +'<div id="peServicesTable" style="max-height:260px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"><div style="padding:12px;color:#94a3b8;font-size:12px">Laddar...</div></div>'
        +'</div>'
        +'<div>'
        +'<h3 style="font-size:14px;font-weight:700;margin:0 0 8px;color:#1a1a1a">Kopplade tillbehör</h3>'
        +'<div id="peAccessoriesTable" style="max-height:260px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"><div style="padding:12px;color:#94a3b8;font-size:12px">Laddar...</div></div>'
        +'</div>'
        +'</div>'
        +'<div id="peUsedByBlock" style="margin-top:16px;display:none">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0 0 8px;color:#1a1a1a">Denna produkt är kopplad som tillval till</h3>'
        +'<div id="peUsedByTable" style="max-height:200px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"></div>'
        +'</div>'
        +'</div>'

        // Flik: Bilder
        +'<div id="peTabBilder" class="pe-tab-panel" style="display:none">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0 0 8px;color:#1a1a1a">Galleri</h3>'
        +'<div id="peGalleryGrid" style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:12px"></div>'
        +'<button onclick="document.getElementById(\'peGalleryInput\').click()" style="padding:8px 16px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">+ Lägg till bilder</button>'
        +'<input type="file" id="peGalleryInput" accept="image/*" multiple style="display:none" onchange="peUploadGalleryImages(\''+p.id+'\')">'
        +'</div>'

        // Flik: Specifikation
        +'<div id="peTabSpec" class="pe-tab-panel" style="display:none">'
        +'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0;color:#1a1a1a">Specifikationer</h3>'
        +'<button onclick="peAddAttribute()" style="padding:5px 12px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">+ Lägg till</button>'
        +'</div>'
        +'<div id="peAttributesTable" style="max-height:400px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px"></div>'
        +'</div>'

        // Flik: Dokument
        +'<div id="peTabDokument" class="pe-tab-panel" style="display:none">'
        +'<h3 style="font-size:14px;font-weight:700;margin:0 0 8px;color:#1a1a1a">Dokument</h3>'
        +'<div id="peDocumentsList" style="max-height:300px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px;margin-bottom:12px"></div>'
        +'<div style="display:flex;gap:8px;align-items:center">'
        +'<select id="peDocType" style="padding:6px 10px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit"><option value="manual">Manual</option><option value="datablad">Datablad</option><option value="certifikat">Certifikat</option><option value="ovrigt">Övrigt</option></select>'
        +'<button onclick="document.getElementById(\'peDocInput\').click()" style="padding:8px 16px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">+ Ladda upp dokument</button>'
        +'<input type="file" id="peDocInput" accept=".pdf,.doc,.docx" style="display:none" onchange="peUploadDocument(\''+p.id+'\')">'
        +'</div>'
        +'</div>'

        +'</div>'

        +'</div></div>';

    document.body.appendChild(modal);
    peRenderVariants();
    peLoadServices(p.id);
    peLoadUsedBy(p.id);
    _peCroppedFile = null;
    peInitGallery(p.id, p.gallery);
    peInitAttributes(p.specs);
    peInitRules(p.specs);
    peInitDocuments(p.documents);
    peRenderRules();
}

let _peSpecsType = '';
let _peStyles = [];
let _peSizes = [];
let _peSpecsMeta = {};
let _peWinModels = []; // Dynamic model columns for window_sizes

function peRenderVariants() {
    const el = document.getElementById('peVariantsTable');
    if(!el) return;

    // === STYLE + WIDTH VARIANTS ===
    if(_peSpecsType === 'style_width_variants' && _peStyles.length) {
        let html = '<div style="max-height:500px;overflow-y:auto">';
        _peStyles.forEach((style, si) => {
            const isOpen = _peStyleExpanded && _peStyleExpanded[si];
            const pricesWithVal = style.variants.filter(v=>v.price!=null).length;
            const totalVars = style.variants.length;
            html += '<div style="margin-bottom:8px;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden">'
                +'<div style="padding:8px 12px;background:#f8f9fa;display:flex;justify-content:space-between;align-items:center;cursor:pointer" onclick="peToggleStyle('+si+')">'
                +'<div style="display:flex;align-items:center;gap:8px">'
                +'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2.5" style="transition:transform .2s;transform:rotate('+(isOpen?'0':'-90')+'deg)"><polyline points="6 9 12 15 18 9"/></svg>'
                +'<strong style="font-size:13px">'+style.style+'</strong>'
                +'<span style="font-size:11px;color:#94a3b8">'+(style.desc||'')+'</span>'
                +'<span style="font-size:10px;padding:2px 8px;border-radius:10px;background:#e0f2fe;color:#0284c7;font-weight:600">'+totalVars+' varianter</span>'
                +(pricesWithVal<totalVars?'<span style="font-size:10px;padding:2px 8px;border-radius:10px;background:#fef3c7;color:#d97706;font-weight:600">'+(totalVars-pricesWithVal)+' saknar pris</span>':'')
                +'</div>'
                +'<button onclick="event.stopPropagation();peAddStyleVariant('+si+')" style="padding:3px 10px;background:#024550;color:#fff;border:none;border-radius:4px;font-size:11px;cursor:pointer;font-family:inherit">+ Bredd</button>'
                +'</div>'
                +'<div id="peStyleBody-'+si+'" style="'+(isOpen?'':'display:none')+'">'
                +'<table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#f1f5f9">'
                +'<th style="padding:4px 8px;text-align:left;font-weight:600;color:#64748b">Bredd (mm)</th>'
                +'<th style="padding:4px 8px;text-align:right;font-weight:600;color:#64748b">Pris (kr)</th>'
                +'<th style="padding:4px 8px;text-align:left;font-weight:600;color:#64748b">Label</th>'
                +'<th style="padding:4px 4px;width:30px"></th>'
                +'</tr></thead><tbody>'
                + style.variants.map((v, vi) => '<tr style="border-top:1px solid #f1f5f9'+(v.price==null?';background:#fffbeb':'')+'">'
                    +'<td style="padding:2px 4px"><input type="number" value="'+(v.width_mm||v.length_mm||'')+'" onchange="peUpdateStyleVar('+si+','+vi+',\'width_mm\',this.value)" style="width:80px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit"></td>'
                    +'<td style="padding:2px 4px;text-align:right"><input type="number" step="0.01" value="'+(v.price!=null?v.price:'')+'" placeholder="–" onchange="peUpdateStyleVar('+si+','+vi+',\'price\',this.value)" style="width:90px;padding:4px 6px;border:1px solid '+(v.price==null?'#fbbf24':'#e5e7eb')+';border-radius:4px;font-size:12px;font-family:inherit;text-align:right"></td>'
                    +'<td style="padding:2px 4px"><input type="text" value="'+(v.label||'')+'" onchange="peUpdateStyleVar('+si+','+vi+',\'label\',this.value)" style="width:100%;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit;box-sizing:border-box"></td>'
                    +'<td style="padding:2px 4px"><button onclick="peRemoveStyleVar('+si+','+vi+')" style="background:none;border:none;cursor:pointer;color:#dc2626;font-size:16px;padding:2px 4px">×</button></td>'
                    +'</tr>').join('')
                +'</tbody></table></div></div>';
        });
        html += '<button onclick="peAddStyle()" style="margin-top:8px;padding:6px 14px;background:#f8f9fa;border:1px dashed #e5e7eb;border-radius:6px;font-size:12px;cursor:pointer;font-family:inherit;color:#64748b">+ Ny stil</button>';
        html += '</div>';
        el.innerHTML = html;
        return;
    }

    // === WIDTH VARIANTS (no styles) ===
    if(_peSpecsType === 'width_variants' && _peVariants.length) {
        el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#f8f9fa;position:sticky;top:0">'
            +'<th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Bredd/Längd (mm)</th>'
            +'<th style="padding:6px 8px;text-align:right;font-weight:600;color:#64748b">Pris (kr)</th>'
            +'<th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Label</th>'
            +'<th style="padding:6px 4px;width:30px"></th>'
            +'</tr></thead><tbody>'
            + _peVariants.map((v, i) => '<tr style="border-top:1px solid #f1f5f9">'
                +'<td style="padding:2px 4px"><input type="number" value="'+(v.width_mm||v.length_mm||'')+'" onchange="peUpdateVariant('+i+',\''+(v.width_mm!==undefined?'width_mm':'length_mm')+'\',this.value)" style="width:100px;padding:5px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit"></td>'
                +'<td style="padding:2px 4px;text-align:right"><input type="number" step="0.01" value="'+(v.price!=null?v.price:'')+'" placeholder="–" onchange="peUpdateVariant('+i+',\'price\',this.value)" style="width:100px;padding:5px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit;text-align:right"></td>'
                +'<td style="padding:2px 4px"><input type="text" value="'+(v.label||'')+'" onchange="peUpdateVariant('+i+',\'label\',this.value)" style="width:100%;padding:5px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit;box-sizing:border-box"></td>'
                +'<td style="padding:2px 4px"><button onclick="peRemoveVariant('+i+')" style="background:none;border:none;cursor:pointer;color:#dc2626;font-size:16px;padding:2px 4px">×</button></td>'
                +'</tr>').join('')
            +'</tbody></table>';
        return;
    }

    // === WINDOW SIZES (with dynamic model columns) ===
    if(_peSpecsType === 'window_sizes' && _peSizes.length) {
        var models = _peWinModels.length ? _peWinModels : [{key:'price',label:'Pris'}];
        var hasModels = _peWinModels.length > 0;
        const widths = [...new Set(_peSizes.map(s=>s.w))].sort((a,b)=>a-b);
        const filterW = _peWinFilterW || '';
        const filtered = filterW ? _peSizes.filter(s => s.w === parseInt(filterW)) : _peSizes;
        var thModels = models.map(function(m,mi){
            return '<th style="padding:5px 4px;text-align:right;font-weight:600;color:#024550;font-size:10px;min-width:70px;position:relative">'
                +m.label
                +(hasModels ? '<button onclick="peRemoveWinModel('+mi+')" style="position:absolute;top:0;right:2px;background:none;border:none;color:#dc2626;cursor:pointer;font-size:10px;padding:0" title="Ta bort modell">\u00d7</button>' : '')
                +'</th>';
        }).join('');
        var bodyRows = filtered.map(function(s,i){
            var realIdx = _peSizes.indexOf(s);
            var priceCells = models.map(function(m){
                var val = s[m.key];
                return '<td style="padding:1px 2px"><input type="number" step="1" value="'+(val!=null&&val!==''?val:'')+'" placeholder="\u2013" onchange="peUpdateWinSize('+realIdx+',\''+m.key+'\',this.value)" style="width:70px;padding:3px 4px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>';
            }).join('');
            return '<tr style="border-top:1px solid #f1f5f9">'
                +'<td style="padding:2px 4px;text-align:right;font-weight:600;font-size:11px">'+s.w+'</td>'
                +'<td style="padding:2px 4px;text-align:right;font-weight:600;font-size:11px">'+s.h+'</td>'
                +priceCells
                +'<td style="padding:2px 2px"><button onclick="peRemoveWinSize('+realIdx+')" style="background:none;border:none;cursor:pointer;color:#dc2626;font-size:13px;padding:0">\u00d7</button></td>'
                +'</tr>';
        }).join('');
        el.innerHTML = '<div>'
            +'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;gap:8px;flex-wrap:wrap">'
            +'<div style="display:flex;align-items:center;gap:8px">'
            +'<span style="font-size:12px;font-weight:600;color:#334155">'+_peSizes.length+' storlekar</span>'
            +'<select id="peWinFilterW" onchange="_peWinFilterW=this.value;peRenderVariants()" style="padding:4px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:11px;font-family:inherit">'
            +'<option value="">Alla bredder</option>'
            +widths.map(w=>'<option value="'+w+'"'+(filterW==w?' selected':'')+'>'+w+' dm</option>').join('')
            +'</select>'
            +'<span style="font-size:11px;color:#94a3b8">Visar '+filtered.length+' st</span>'
            +'</div>'
            +'<div style="display:flex;gap:6px;flex-wrap:wrap">'
            +'<button onclick="peAddWinModel()" style="padding:4px 10px;background:#059669;color:#fff;border:none;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;font-family:inherit">+ L\u00e4gg till modell</button>'
            +'<label style="padding:4px 10px;background:#024550;color:#fff;border:none;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:4px">'
            +'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'
            +'Importera Excel'
            +'<input type="file" accept=".xlsx,.xls,.csv" style="display:none" onchange="peImportWindowExcel(this)">'
            +'</label>'
            +'<button onclick="peAddWindowSize()" style="padding:4px 10px;background:#f8f9fa;border:1px solid #e5e7eb;border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;font-family:inherit">+ L\u00e4gg till storlek</button>'
            +'</div></div>'
            +'<div style="max-height:500px;overflow:auto;border:1px solid #e5e7eb;border-radius:8px">'
            +'<table style="width:100%;border-collapse:collapse;font-size:11px">'
            +'<thead><tr style="background:#f8f9fa;position:sticky;top:0;z-index:1">'
            +'<th style="padding:5px 6px;text-align:right;font-weight:600;color:#64748b">Bredd</th>'
            +'<th style="padding:5px 6px;text-align:right;font-weight:600;color:#64748b">H\u00f6jd</th>'
            +thModels
            +'<th style="padding:5px 2px;width:20px"></th>'
            +'</tr></thead><tbody>'
            +bodyRows
            +'</tbody></table></div></div>';
        return;
    }

    // === DEFAULT (old format) ===
    if(!_peVariants.length) {
        el.innerHTML = '<div style="padding:12px;color:#94a3b8;font-size:12px;text-align:center">Inga varianter. Klicka "+ Lägg till" för att skapa.</div>';
        return;
    }
    const keys = Object.keys(_peVariants[0]);
    el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px">'
        +'<thead><tr style="background:#f8f9fa;position:sticky;top:0">'
        + keys.map(k=>'<th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b;white-space:nowrap">'+k+'</th>').join('')
        +'<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:60px">Förvald</th>'
        +'<th style="padding:6px 4px;width:30px"></th>'
        +'</tr></thead><tbody>'
        + _peVariants.map((v,i)=>'<tr style="border-top:1px solid #f1f5f9">'
            + keys.map(k=>'<td style="padding:2px 4px"><input type="'+(typeof v[k]==='number'?'number':'text')+'" value="'+(v[k]!=null?v[k]:'')+'" onchange="peUpdateVariant('+i+',\''+k+'\',this.value)" style="width:100%;padding:5px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit;box-sizing:border-box"></td>').join('')
            +'<td style="padding:2px 4px;text-align:center"><input type="checkbox" '+(v.is_default?'checked':'')+' onchange="peSetDefaultVariant('+i+',this.checked)" style="width:16px;height:16px;accent-color:#024550"></td>'
            +'<td style="padding:2px 4px"><button onclick="peRemoveVariant('+i+')" style="background:none;border:none;cursor:pointer;color:#dc2626;font-size:16px;padding:2px 4px" title="Ta bort">×</button></td>'
            +'</tr>').join('')
        +'</tbody></table>';
}

let _peWinFilterW = '';

function peUpdateWinSize(idx, key, val) {
    if(key === 'note' || key === 'article_id') { _peSizes[idx][key] = val; }
    else { var n = parseFloat(val); _peSizes[idx][key] = val===''?null:(isNaN(n)?null:n); }
}
function peRemoveWinSize(idx) { _peSizes.splice(idx, 1); peRenderVariants(); }
function peAddWinModel() {
    var name = prompt('Modellnamn (t.ex. Fast, 1-Luft, 2-Luft, Sidoh\u00e4ngd):');
    if(!name) return;
    var key = name.toLowerCase().replace(/[^a-z0-9]/g,'_').replace(/_+/g,'_');
    if(_peWinModels.find(function(m){return m.key===key;})) { alert('Modellen finns redan'); return; }
    // If first model and legacy 'price' data exists, migrate it
    if(_peWinModels.length === 0 && _peSizes.length && _peSizes[0].price !== undefined) {
        // Ask what the existing price column should be called
        var oldName = prompt('Befintliga priser \u00e4r sparade som \"Pris\". Vad ska den kolumnen heta? (t.ex. Fast)', 'Fast');
        if(oldName) {
            var oldKey = oldName.toLowerCase().replace(/[^a-z0-9]/g,'_').replace(/_+/g,'_');
            _peWinModels.push({key: oldKey, label: oldName});
            // Migrate price -> oldKey
            _peSizes.forEach(function(s){ if(s.price !== undefined) { s[oldKey] = s.price; delete s.price; } });
        }
    }
    _peWinModels.push({key: key, label: name});
    peRenderVariants();
}
function peRemoveWinModel(idx) {
    if(!confirm('Ta bort modell \u201c' + _peWinModels[idx].label + '\u201d och alla dess priser?')) return;
    var key = _peWinModels[idx].key;
    _peSizes.forEach(function(s){ delete s[key]; });
    _peWinModels.splice(idx, 1);
    peRenderVariants();
}
function peAddWindowSize() {
    _peSizes.push({w:0, h:0, price:null, note:'', article_id:''});
    peRenderVariants();
}
function peImportWindowExcel(input) {
    const file = input.files[0];
    if(!file) return;
    const reader = new FileReader();
    reader.onload = function(e) {
        try {
            // Parse CSV (simple) or show message for xlsx
            if(file.name.endsWith('.csv')) {
                const lines = e.target.result.split('\n').filter(l=>l.trim());
                const header = lines[0].split(',').map(h=>h.trim().toLowerCase());
                let imported = 0;
                for(let i=1; i<lines.length; i++) {
                    const cols = lines[i].split(',').map(c=>c.trim());
                    if(cols.length < 4) continue;
                    const w = parseInt(cols[header.indexOf('width')]||cols[2]) || 0;
                    const h = parseInt(cols[header.indexOf('height')]||cols[3]) || 0;
                    const price = parseFloat(cols[header.indexOf('price')]||cols[6]) || null;
                    const note = cols[header.indexOf('note')]||cols[7]||'';
                    const artId = cols[header.indexOf('modellid')]||cols[0]||'';
                    // Update existing or add
                    const existing = _peSizes.find(s => s.w === w && s.h === h);
                    if(existing) { existing.price = price; if(note) existing.note = note; if(artId) existing.article_id = parseInt(artId)||artId; }
                    else { _peSizes.push({w, h, price, note, article_id: parseInt(artId)||artId}); }
                    imported++;
                }
                alert('Importerade/uppdaterade ' + imported + ' rader');
            } else {
                alert('Excel-import (.xlsx) kräver att filen först sparas som CSV.\n\nI Excel: Arkiv → Spara som → CSV (kommaavgränsad)\n\nKolumner: ModellID, ProduktTyp, Width, Height, Bredd, Höjd, Price, Note');
            }
        } catch(err) { alert('Importfel: ' + err.message); }
        peRenderVariants();
        input.value = '';
    };
    if(file.name.endsWith('.csv')) reader.readAsText(file);
    else reader.readAsArrayBuffer(file);
}

let _peStyleExpanded = {};
function peToggleStyle(si) {
    _peStyleExpanded[si] = !_peStyleExpanded[si];
    const body = document.getElementById('peStyleBody-'+si);
    const arrow = body?.previousElementSibling?.querySelector('svg');
    if(body) body.style.display = _peStyleExpanded[si] ? '' : 'none';
    if(arrow) arrow.style.transform = 'rotate('+(_peStyleExpanded[si]?'0':'-90')+'deg)';
}

function peUpdateStyleVar(si, vi, key, val) {
    if(key === 'price') { const n = parseFloat(val); _peStyles[si].variants[vi][key] = val===''?null:(isNaN(n)?null:n); }
    else if(key === 'width_mm' || key === 'length_mm') { _peStyles[si].variants[vi][key] = parseInt(val)||0; }
    else { _peStyles[si].variants[vi][key] = val; }
}
function peRemoveStyleVar(si, vi) { _peStyles[si].variants.splice(vi, 1); peRenderVariants(); }
function peAddStyleVariant(si) { _peStyles[si].variants.push({width_mm:0,price:null}); peRenderVariants(); }
function peAddStyle() { _peStyles.push({style:'Ny stil',desc:'',variants:[{width_mm:0,price:null}]}); peRenderVariants(); }

function peAddVariant() {
    if(_peVariants.length) {
        const tmpl = {};
        Object.keys(_peVariants[0]).forEach(k=>tmpl[k]=0);
        _peVariants.push(tmpl);
    } else {
        _peVariants.push({label:'',pris:0});
    }
    peRenderVariants();
}

function peSetDefaultVariant(idx, checked) {
    _peVariants.forEach((v,i) => { v.is_default = (i === idx && checked) ? 1 : 0; });
    peRenderVariants();
}
function peRemoveVariant(i) {
    _peVariants.splice(i, 1);
    peRenderVariants();
}

function peUpdateVariant(i, key, val) {
    const num = parseFloat(val);
    _peVariants[i][key] = isNaN(num) ? val : num;
}

let _peAccessories = [];

function peLinkNumber(value) {
    if (value === '' || value === null || value === undefined) return '';
    const num = parseFloat(value);
    return Number.isNaN(num) ? '' : num;
}

function peCreateLinkState(product, linkedCfg) {
    const cfg = linkedCfg || null;
    return {
        id: product.id,
        name: product.name,
        price: parseFloat(product.price),
        linked: !!cfg,
        default_on: parseInt(cfg?.default_on || 0),
        hidden: parseInt(cfg?.hidden || 0),
        mandatory: parseInt(cfg?.mandatory || 0),
        default_variant: cfg?.default_variant || '',
        default_qty: peLinkNumber(cfg?.default_qty),
        min_qty: peLinkNumber(cfg?.min_qty),
        max_qty: peLinkNumber(cfg?.max_qty)
    };
}

function peUpdateLinkItem(listName, idx, field, value) {
    const list = listName === 'services' ? _peServices : _peAccessories;
    if (!list[idx]) return;
    if (field === 'linked') {
        list[idx].linked = !!value;
        if (!value) {
            list[idx].default_on = 0;
            list[idx].hidden = 0;
            list[idx].mandatory = 0;
            list[idx].default_variant = '';
            list[idx].default_qty = '';
            list[idx].min_qty = '';
            list[idx].max_qty = '';
        }
    } else if (['default_on', 'hidden', 'mandatory'].includes(field)) {
        list[idx][field] = value ? 1 : 0;
    } else {
        list[idx][field] = value === '' ? '' : value;
    }
    if (listName === 'services') peRenderServices();
    else peRenderAccessories();
}

function peRenderLinkTable(items, listName, title) {
    if (!items.length) {
        return '<div style="padding:12px;color:#94a3b8;font-size:12px">Inga ' + title.toLowerCase() + '</div>';
    }
    const sortedItems = [...items].sort(function(a, b) {
        if (!!a.linked !== !!b.linked) return a.linked ? -1 : 1;
        return a.name.localeCompare(b.name, 'sv');
    });
    const accent = listName === 'services' ? '#059669' : '#0284c7';
    return '<table style="width:100%;border-collapse:collapse;font-size:12px">'
        + '<thead><tr style="background:#f8f9fa;position:sticky;top:0">'
        + '<th style="padding:6px 8px;width:30px"></th>'
        + '<th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">' + title + '</th>'
        + '<th style="padding:6px 8px;text-align:right;font-weight:600;color:#64748b">Pris</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:70px">Förvald</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:80px">Oblig.</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:55px">Dold</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Antal</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Min</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Max</th>'
        + '</tr></thead><tbody>'
        + sortedItems.map(function(item) {
            const i = items.indexOf(item);
            const linked = !!item.linked;
            const disabled = linked ? '' : ' disabled';
            const disabledStyle = linked ? '' : ';opacity:.45';
            return '<tr style="border-top:1px solid #f1f5f9' + (linked ? ';background:#f0fdf4' : '') + '">'
                + '<td style="padding:4px 8px"><input type="checkbox" ' + (linked ? 'checked' : '') + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'linked\',this.checked)" style="width:15px;height:15px;accent-color:' + accent + '"></td>'
                + '<td style="padding:4px 8px"><button type="button" onclick="event.stopPropagation();editProduct(\'' + item.id + '\')" style="background:none;border:none;padding:0;margin:0;color:#024550;font-weight:700;cursor:pointer;font-size:12px;font-family:inherit;text-align:left">' + item.name + '</button></td>'
                + '<td style="padding:4px 8px;text-align:right">' + item.price.toLocaleString('sv-SE') + ' kr</td>'
                + '<td style="padding:4px 8px;text-align:center' + disabledStyle + '"><input type="checkbox" ' + (item.default_on ? 'checked' : '') + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'default_on\',this.checked)" style="width:15px;height:15px;accent-color:#059669"></td>'
                + '<td style="padding:4px 8px;text-align:center' + disabledStyle + '"><input type="checkbox" ' + (item.mandatory ? 'checked' : '') + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'mandatory\',this.checked)" style="width:15px;height:15px;accent-color:#f59e0b"></td>'
                + '<td style="padding:4px 8px;text-align:center' + disabledStyle + '"><input type="checkbox" ' + (item.hidden ? 'checked' : '') + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'hidden\',this.checked)" style="width:15px;height:15px;accent-color:#ef4444"></td>'
                + '<td style="padding:4px 8px' + disabledStyle + '"><input type="number" min="0" step="1" value="' + (item.default_qty ?? '') + '"' + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'default_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '<td style="padding:4px 8px' + disabledStyle + '"><input type="number" min="0" step="1" value="' + (item.min_qty ?? '') + '"' + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'min_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '<td style="padding:4px 8px' + disabledStyle + '"><input type="number" min="0" step="1" value="' + (item.max_qty ?? '') + '"' + disabled + ' onchange="peUpdateLinkItem(\'' + listName + '\',' + i + ',\'max_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '</tr>';
        }).join('')
        + '</tbody></table>';
}

async function peLoadServices(productId) {
    const el = document.getElementById('peServicesTable');
    const elAcc = document.getElementById('peAccessoriesTable');
    try {
        const [svcRes, accRes, linkedRes] = await Promise.all([
            fetch('/api/products.php?cat=tjanster&active=1'),
            fetch('/api/products.php?cat=tillbehor&active=1'),
            fetch('/api/products.php?services_for=' + productId)
        ]);
        const svcData = await svcRes.json();
        const accData = await accRes.json();
        const linkedData = await linkedRes.json();
        const allSvc = svcData.success ? svcData.products : [];
        const allAcc = accData.success ? accData.products : [];
        const linkedMap = {};
        if (linkedData.success) {
            (linkedData.services || []).forEach(s => {
                linkedMap[s.service_id] = {
                    default_on: parseInt(s.default_on || 0),
                    hidden: parseInt(s.hidden || 0),
                    mandatory: parseInt(s.mandatory || 0),
                    default_variant: s.default_variant || '',
                    default_qty: s.default_qty,
                    min_qty: s.min_qty,
                    max_qty: s.max_qty
                };
            });
        }
        _peServices = allSvc.map(s => peCreateLinkState(s, linkedMap[s.id]));
        _peAccessories = allAcc.map(s => peCreateLinkState(s, linkedMap[s.id]));
        peRenderServices();
        peRenderAccessories();
    } catch(e) {
        el.innerHTML = '<div style="padding:12px;color:#ef4444;font-size:12px">Kunde inte ladda tjänster</div>';
        if(elAcc) elAcc.innerHTML = '<div style="padding:12px;color:#ef4444;font-size:12px">Kunde inte ladda tillbehör</div>';
    }
}

function peRenderServices() {
    const el = document.getElementById('peServicesTable');
    el.innerHTML = peRenderLinkTable(_peServices, 'services', 'Tjänst');
}

function peRenderAccessories() {
    const el = document.getElementById('peAccessoriesTable');
    if(!el) return;
    el.innerHTML = peRenderLinkTable(_peAccessories, 'accessories', 'Tillbehör');
}

// === KOPPLAD TILL (reverse lookup med per-link flags) ===
var _peUsedByData = [];
var _peUsedByServiceId = '';
var _peUsedByVariants = [];

function peUpdateLinkFlag(productId, serviceId, field, value) {
    var sendValue;
    if (field === 'default_variant') sendValue = value;
    else if (['default_qty', 'min_qty', 'max_qty'].includes(field)) sendValue = value === '' ? null : value;
    else sendValue = value ? 1 : 0;
    fetch('/api/products.php?update_link=1', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({product_id: productId, service_id: serviceId, field: field, value: sendValue}) });
}

function peGetVariantsForProduct(productId) {
    var prod = catalogProducts.find(function(x) { return x.id === productId; });
    if (!prod || !prod.specs) return [];
    var specs = typeof prod.specs === 'string' ? JSON.parse(prod.specs) : prod.specs;
    var variants = [];
    if (specs.styles && specs.styles.length > 0) {
        specs.styles.forEach(function(style) {
            if (style.variants && style.variants.length > 0) {
                style.variants.forEach(function(v) {
                    variants.push({label: style.style + ' ' + v.width_mm + 'mm', value: style.style + '|' + v.width_mm, price: v.price});
                });
            } else {
                variants.push({label: style.style, value: style.style, price: null});
            }
        });
    } else if (specs.variants && specs.variants.length > 0) {
        specs.variants.forEach(function(v) {
            var label = Object.entries(v).filter(function(e) { return e[0] !== 'price'; }).map(function(e) { return e[1]; }).join(' ');
            variants.push({label: label, value: label, price: v.price});
        });
    }
    return variants;
}

function peShowVariantPicker(rowIdx, productId) {
    var sid = _peUsedByServiceId;
    var variants = _peUsedByVariants;
    var old = document.getElementById('peVariantPicker'); if (old) old.remove();
    var picker = document.createElement('div');
    picker.id = 'peVariantPicker';
    picker.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px';
    picker.onclick = function(e) { if (e.target === picker) { _peUsedByData[rowIdx].default_on = 0; _peUsedByData[rowIdx].default_variant = null; peUpdateLinkFlag(productId, sid, 'default_on', false); peRenderUsedBy(); picker.remove(); } };
    var currentVariant = _peUsedByData[rowIdx].default_variant || '';
    var html = '<div style="background:#fff;border-radius:12px;padding:20px;max-width:400px;width:100%;max-height:70vh;overflow-y:auto;box-shadow:0 10px 40px rgba(0,0,0,.2)">'
        + '<h3 style="margin:0 0 4px;font-size:15px;font-weight:700">Välj förvald variant</h3>'
        + '<p style="margin:0 0 12px;font-size:12px;color:#94a3b8">för ' + _peUsedByData[rowIdx].name + '</p>'
        + '<div style="display:flex;flex-direction:column;gap:4px">';
    variants.forEach(function(v) {
        var selected = (v.value === currentVariant);
        var priceStr = v.price != null ? ' — ' + parseFloat(v.price).toLocaleString('sv-SE') + ' kr' : '';
        html += '<button onclick="peSelectVariant(' + rowIdx + ',\'' + productId + '\',\'' + v.value.replace(/'/g, "\\'") + '\')" style="padding:8px 12px;border:1px solid ' + (selected ? '#059669' : '#e5e7eb') + ';border-radius:8px;background:' + (selected ? '#f0fdf4' : '#fff') + ';cursor:pointer;text-align:left;font-size:13px;font-family:inherit">' + v.label + '<span style="color:#94a3b8">' + priceStr + '</span></button>';
    });
    html += '</div><button onclick="peSelectVariant(' + rowIdx + ',\'' + productId + '\',null)" style="margin-top:10px;padding:6px 12px;border:1px solid #e5e7eb;border-radius:6px;background:#fff;cursor:pointer;font-size:12px;color:#94a3b8;font-family:inherit">Ingen specifik variant</button></div>';
    picker.innerHTML = html;
    document.body.appendChild(picker);
}

function peSelectVariant(rowIdx, productId, variantValue) {
    var sid = _peUsedByServiceId;
    _peUsedByData[rowIdx].default_on = 1;
    _peUsedByData[rowIdx].default_variant = variantValue;
    peUpdateLinkFlag(productId, sid, 'default_on', true);
    peUpdateLinkFlag(productId, sid, 'default_variant', variantValue);
    peRenderUsedBy();
    var picker = document.getElementById('peVariantPicker'); if (picker) picker.remove();
}

function peHandleDefaultOn(rowIdx, checked, productId) {
    var sid = _peUsedByServiceId;
    if (checked && _peUsedByVariants.length > 0) {
        peShowVariantPicker(rowIdx, productId);
    } else {
        _peUsedByData[rowIdx].default_on = checked ? 1 : 0;
        if (!checked) { _peUsedByData[rowIdx].default_variant = null; peUpdateLinkFlag(productId, sid, 'default_variant', null); }
        peUpdateLinkFlag(productId, sid, 'default_on', checked);
        peRenderUsedBy();
    }
}

function peLoadUsedBy(productId) {
    var block = document.getElementById('peUsedByBlock');
    var el = document.getElementById('peUsedByTable');
    if (!block || !el) return;
    _peUsedByServiceId = productId;
    _peUsedByVariants = peGetVariantsForProduct(productId);
    fetch('/api/products.php?used_by=' + productId).then(function(r) { return r.json(); }).then(function(data) {
        _peUsedByData = data.success ? data.products : [];
        if (!_peUsedByData.length) { block.style.display = 'none'; return; }
        block.style.display = 'block';
        peRenderUsedBy();
        var globOblig = document.getElementById('peTillvalOblig');
        var globHidden = document.getElementById('peTillvalHidden');
        if (globOblig) { globOblig.disabled = true; globOblig.parentElement.style.opacity = '0.4'; }
        if (globHidden) { globHidden.disabled = true; globHidden.parentElement.style.opacity = '0.4'; }
        var tillvalLabel = document.querySelector('[data-tillval-label]');
        if (tillvalLabel) tillvalLabel.innerHTML = 'NÄR PRODUKTEN ANVÄNDS SOM TILLVAL <span style="font-weight:400;color:#b0b0b0">(hanteras per koppling)</span>';
    });
}

function peRenderUsedBy() {
    var el = document.getElementById('peUsedByTable');
    if (!el) return;
    var sid = _peUsedByServiceId;
    var hasVariants = _peUsedByVariants.length > 0;
    el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px">'
        + '<thead><tr style="background:#f8f9fa;position:sticky;top:0">'
        + '<th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Produkt</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:' + (hasVariants ? '140' : '70') + 'px">Förvald</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:80px">Obligatorisk</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:50px">Dold</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Antal</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Min</th>'
        + '<th style="padding:6px 8px;text-align:center;font-weight:600;color:#64748b;width:68px">Max</th>'
        + '</tr></thead><tbody>'
        + _peUsedByData.map(function(p, i) {
            var defOn = parseInt(p.default_on) === 1;
            var mand = parseInt(p.mandatory) === 1;
            var hid = parseInt(p.hidden) === 1;
            var defVar = p.default_variant || '';
            var variantLabel = '';
            if (defOn && defVar && hasVariants) {
                var found = _peUsedByVariants.find(function(v) { return v.value === defVar; });
                variantLabel = found ? found.label : defVar;
            }
            return '<tr style="border-top:1px solid #f1f5f9">'
                + '<td style="padding:5px 8px"><div style="font-weight:500">' + p.name + '</div><div style="font-size:11px;color:#94a3b8">' + (p.cat_label || '') + '</div></td>'
                + '<td style="padding:5px 8px;text-align:center"><div style="display:flex;align-items:center;justify-content:center;gap:4px">'
                + '<input type="checkbox" ' + (defOn ? 'checked' : '') + ' onchange="peHandleDefaultOn(' + i + ',this.checked,\'' + p.id + '\')" style="width:16px;height:16px;accent-color:#059669;cursor:pointer">'
                + (defOn && variantLabel ? '<span onclick="peShowVariantPicker(' + i + ',\'' + p.id + '\')" style="font-size:10px;color:#059669;cursor:pointer;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + variantLabel + '">' + variantLabel + '</span>' : '')
                + '</div></td>'
                + '<td style="padding:5px 8px;text-align:center"><input type="checkbox" ' + (mand ? 'checked' : '') + ' onchange="_peUsedByData[' + i + '].mandatory=this.checked?1:0;peUpdateLinkFlag(\'' + p.id + '\',\'' + sid + '\',\'mandatory\',this.checked)" style="width:16px;height:16px;accent-color:#f59e0b;cursor:pointer"></td>'
                + '<td style="padding:5px 8px;text-align:center"><input type="checkbox" ' + (hid ? 'checked' : '') + ' onchange="_peUsedByData[' + i + '].hidden=this.checked?1:0;peUpdateLinkFlag(\'' + p.id + '\',\'' + sid + '\',\'hidden\',this.checked)" style="width:16px;height:16px;accent-color:#ef4444;cursor:pointer"></td>'
                + '<td style="padding:5px 8px"><input type="number" min="0" step="1" value="' + (p.default_qty ?? '') + '" onchange="_peUsedByData[' + i + '].default_qty=this.value;peUpdateLinkFlag(\'' + p.id + '\',\'' + sid + '\',\'default_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '<td style="padding:5px 8px"><input type="number" min="0" step="1" value="' + (p.min_qty ?? '') + '" onchange="_peUsedByData[' + i + '].min_qty=this.value;peUpdateLinkFlag(\'' + p.id + '\',\'' + sid + '\',\'min_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '<td style="padding:5px 8px"><input type="number" min="0" step="1" value="' + (p.max_qty ?? '') + '" onchange="_peUsedByData[' + i + '].max_qty=this.value;peUpdateLinkFlag(\'' + p.id + '\',\'' + sid + '\',\'max_qty\',this.value)" style="width:58px;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;text-align:right;font-family:inherit"></td>'
                + '</tr>';
        }).join('')
        + '</tbody></table>';
}

// === FLIK-SYSTEM ===
function peSetTab(tab) {
    document.querySelectorAll('.pe-tab-panel').forEach(p => p.style.display = 'none');
    document.querySelectorAll('.pe-tab').forEach(b => { b.style.borderBottomColor = 'transparent'; b.style.color = '#64748b'; });
    const panel = document.getElementById('peTab' + tab.charAt(0).toUpperCase() + tab.slice(1));
    if(panel) panel.style.display = 'block';
    const btn = document.querySelector('.pe-tab[data-tab="'+tab+'"]');
    if(btn) { btn.style.borderBottomColor = '#024550'; btn.style.color = '#024550'; }

    // Ladda galleri/spec/dokument vid första öppning
    if(tab === 'bilder') peRenderGallery();
    if(tab === 'spec') peRenderAttributes();
    if(tab === 'dokument') peRenderDocuments();
}

// === GALLERI ===
let _peGallery = [];
let _peProductId = '';

function peInitGallery(productId, gallery) {
    _peProductId = productId;
    _peGallery = Array.isArray(gallery) ? [...gallery] : [];
}

function peRenderGallery() {
    const el = document.getElementById('peGalleryGrid');
    if(!el) return;
    if(!_peGallery.length) { el.innerHTML = '<div style="grid-column:1/-1;padding:20px;text-align:center;color:#94a3b8;font-size:12px;border:1px dashed #e5e7eb;border-radius:8px">Inga galleribilder</div>'; return; }
    el.innerHTML = _peGallery.map((img, i) =>
        '<div style="position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;border:1px solid #e5e7eb;cursor:pointer" onclick="peSetMainImage('+i+')" title="Klicka för att sätta som huvudbild">'
        +'<img src="'+img+'" style="width:100%;height:100%;object-fit:cover">'
        +'<button onclick="event.stopPropagation();peRemoveGalleryImage('+i+')" style="position:absolute;top:2px;right:2px;width:20px;height:20px;background:rgba(0,0,0,.6);color:#fff;border:none;border-radius:50%;font-size:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1">&times;</button>'
        +'</div>'
    ).join('');
}

async function peUploadGalleryImages(productId) {
    const input = document.getElementById('peGalleryInput');
    if(!input || !input.files.length) return;
    for(const file of input.files) {
        const fd = new FormData();
        fd.append('id', productId);
        fd.append('image', file);
        try {
            const res = await fetch('/api/products.php?upload=1&gallery=1', { method:'POST', body: fd });
            const data = await res.json();
            if(data.success && data.gallery) {
                _peGallery = data.gallery;
            }
        } catch(e) { console.error('Gallery upload error:', e); }
    }
    peRenderGallery();
    input.value = '';
}

async function peRemoveGalleryImage(idx) {
    const imgPath = _peGallery[idx];
    if(!imgPath) return;
    try {
        const res = await fetch('/api/products.php?remove_gallery=1', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ id: _peProductId, img_path: imgPath })
        });
        const data = await res.json();
        if(data.success) { _peGallery = data.gallery || []; peRenderGallery(); }
    } catch(e) { console.error('Remove gallery error:', e); }
}

async function peSetMainImage(idx) {
    const galleryImg = _peGallery[idx];
    if(!galleryImg) return;
    // Byt: gammal huvudbild → gallery, galleribild → huvudbild
    const mainPreview = document.getElementById('peImgPreview');
    const currentMain = mainPreview?.querySelector('img')?.src || '';
    // Uppdatera preview
    mainPreview.innerHTML = '<img src="'+galleryImg+'" style="width:100%;height:100%;object-fit:cover">';
    // Byt i gallery-array
    _peGallery.splice(idx, 1);
    // Lägg gammal huvudbild i gallery om den finns
    if(currentMain && !currentMain.includes('data:')) {
        // Extrahera relativ path
        const url = new URL(currentMain, window.location.origin);
        const relPath = url.pathname.replace(/^\//, '');
        if(relPath.startsWith('Photo/')) _peGallery.unshift(relPath);
    }
    // Spara direkt till DB
    try {
        await fetch('/api/products.php', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ id: _peProductId, img: galleryImg, gallery: _peGallery })
        });
    } catch(e) { console.error('Set main image error:', e); }
    peRenderGallery();
}

// === SPECIFIKATIONER (nyckel-värde) ===
let _peAttributes = [];

function peInitAttributes(specs) {
    _peAttributes = (specs && specs.attributes) ? [...specs.attributes] : [];
}

function peRenderAttributes() {
    const el = document.getElementById('peAttributesTable');
    if(!el) return;
    if(!_peAttributes.length) { el.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8;font-size:12px">Inga specifikationer. Klicka "+ Lägg till".</div>'; return; }
    el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px">'
        +'<thead><tr style="background:#f8f9fa;position:sticky;top:0"><th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Egenskap</th><th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Värde</th><th style="padding:6px 8px;width:30px"></th></tr></thead><tbody>'
        + _peAttributes.map((a, i) =>
            '<tr style="border-top:1px solid #f1f5f9">'
            +'<td style="padding:4px 6px"><input value="'+((a.key||'').replace(/"/g,'&quot;'))+'" onchange="_peAttributes['+i+'].key=this.value" style="width:100%;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit" placeholder="t.ex. Vikt"></td>'
            +'<td style="padding:4px 6px"><input value="'+((a.value||'').replace(/"/g,'&quot;'))+'" onchange="_peAttributes['+i+'].value=this.value" style="width:100%;padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:12px;font-family:inherit" placeholder="t.ex. 45 kg"></td>'
            +'<td style="padding:4px 6px;text-align:center"><button onclick="_peAttributes.splice('+i+',1);peRenderAttributes()" style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:14px">&times;</button></td>'
            +'</tr>'
        ).join('')
        +'</tbody></table>';
}

function peAddAttribute() {
    _peAttributes.push({ key: '', value: '' });
    peRenderAttributes();
}

// === REGLER (tillval ↔ varianter) ===
let _peRules = [];

function peInitRules(specs) {
    _peRules = (specs && specs.rules) ? [...specs.rules] : [];
}

function peGetVariantFields() {
    if(_peSpecsType === 'window_sizes' && _peSizes.length) return ['w','h','price','note','article_id'];
    if(_peSpecsType === 'style_width_variants' && _peStyles.length) return ['style','width_mm','price'];
    if(_peSpecsType === 'width_variants' && _peVariants.length) return ['width_mm','price','label'];
    if(!_peVariants.length) return [];
    return Object.keys(_peVariants[0]).filter(k => k !== '__idx');
}

function peGetTillvalOptions() {
    const svc = (_peServices || []).filter(s => s.linked).map(s => ({id: s.id, name: s.name}));
    const acc = (_peAccessories || []).filter(s => s.linked).map(s => ({id: s.id, name: s.name}));
    return [...svc, ...acc];
}

function peRenderRules() {
    const el = document.getElementById('peRulesTable');
    if(!el) return;
    const fields = peGetVariantFields();
    const tillval = peGetTillvalOptions();
    const operators = [{v:'*',l:'Alla'},{v:'==',l:'='},{v:'!=',l:'≠'},{v:'<',l:'<'},{v:'<=',l:'≤'},{v:'>',l:'>'},{v:'>=',l:'≥'}];
    const actions = [
        {v:'max',l:'Max antal',g:'Antal'},
        {v:'min',l:'Min antal',g:'Antal'},
        {v:'berakna_antal',l:'Beräkna antal',g:'Antal'},
        {v:'kraver',l:'Kräver',g:'Villkor'},
        {v:'exkluderar',l:'Exkluderar',g:'Villkor'},
        {v:'inkluderar',l:'Inkluderar',g:'Villkor'},
        {v:'satt_pris',l:'Sätt pris',g:'Pris'},
        {v:'pris_tillagg',l:'Pristillägg (+/-)',g:'Pris'},
        {v:'pris_per_enhet',l:'Pris × enhet',g:'Pris'},
        {v:'pris_multiplikator',l:'Pris × faktor',g:'Pris'},
        {v:'satt_langd',l:'Längd (mm)',g:'Dimension'},{v:'satt_bredd',l:'Bredd (mm)',g:'Dimension'},{v:'satt_grundpris',l:'Sätt produktpris',g:'Produkt'}
    ];

    let html = '';
    if(!_peRules.length) {
        html = '<div style="padding:16px;text-align:center;color:#94a3b8;font-size:12px">Inga regler. Klicka "+ Lägg till".</div>';
    } else {
        html = _peRules.map((r, i) => {
            const fieldOpts = fields.map(f => '<option value="'+f+'"'+(f===r.field?' selected':'')+'>'+f+'</option>').join('');
            const opOpts = operators.map(o => '<option value="'+o.v+'"'+(o.v===r.operator?' selected':'')+'>'+o.l+'</option>').join('');
            const tvOpts = tillval.map(t => '<option value="'+t.id+'"'+(t.id===r.tillval_id?' selected':'')+'>'+t.name+'</option>').join('');
            const groups = {};
            actions.forEach(a => { if(!groups[a.g]) groups[a.g]=[]; groups[a.g].push(a); });
            const actOpts = Object.entries(groups).map(([g,acts]) => '<optgroup label="'+g+'">'+acts.map(a=>'<option value="'+a.v+'"'+(a.v===r.action?' selected':'')+'>'+a.l+'</option>').join('')+'</optgroup>').join('');
            const s = 'padding:4px 6px;border:1px solid #e5e7eb;border-radius:4px;font-size:11px;font-family:inherit;background:#fff';
            const isProductAction = r.action === 'satt_grundpris';
            const isDimension = r.action==='satt_langd'||r.action==='satt_bredd';
            const needsFormula = ['berakna_antal','pris_per_enhet','pris_multiplikator','satt_grundpris','satt_langd','satt_bredd'].includes(r.action);
            const chainOpHtml = isDimension ? '<select onchange="_peRules['+i+'].chain_op=this.value" style="padding:3px 2px;border:1.5px solid #bae6fd;border-radius:4px;font-size:13px;font-weight:700;text-align:center;color:#0284c7;background:#f0f9ff;width:36px;font-family:inherit;cursor:pointer"><option value="="'+((r.chain_op||'=')=='='?' selected':'')+'>=</option><option value="+"'+(r.chain_op=='+'?' selected':'')+'>+</option><option value="-"'+(r.chain_op=='-'?' selected':'')+'>&minus;</option><option value="*"'+(r.chain_op=='*'?' selected':'')+'>&times;</option></select>' : '<span style="font-size:10px;color:#64748b">=</span>';
            return '<div style="border:1px solid #e5e7eb;border-radius:8px;padding:10px;margin-bottom:6px;background:#fafbfc;position:relative">'
                +'<div style="position:absolute;top:6px;left:10px;font-size:10px;font-weight:700;color:#cbd5e1">'+(i+1)+'</div>'
                +'<button onclick="_peRules.splice('+i+',1);peRenderRules()" style="position:absolute;top:4px;right:6px;background:none;border:none;color:#ef4444;cursor:pointer;font-size:14px">&times;</button>'
                +'<div style="display:grid;grid-template-columns:28px 1fr 50px 60px;gap:4px;align-items:center;margin-bottom:6px">'
                +'<span style="font-size:10px;font-weight:700;color:#64748b;text-transform:uppercase">OM</span>'
                +'<select onchange="_peRules['+i+'].field=this.value;peRenderRules()" style="'+s+'"><option value="">Alla</option>'+fieldOpts+'</select>'
                +'<select onchange="_peRules['+i+'].operator=this.value" style="'+s+'">'+opOpts+'</select>'
                +'<input value="'+((r.value||'').toString().replace(/"/g,'&quot;'))+'" onchange="_peRules['+i+'].value=this.value" style="'+s+'" placeholder="\u2014">'
                +'</div>'
                +'<div style="display:grid;grid-template-columns:28px 1fr 36px 1fr;gap:4px;align-items:center">'
                +'<span style="font-size:10px;font-weight:700;color:#64748b;text-transform:uppercase">S\u00c5</span>'
                +'<select onchange="_peRules['+i+'].action=this.value;peRenderRules()" style="'+s+'">'+actOpts+'</select>'
                +chainOpHtml
                +'<input value="'+((r.action_value||'').toString().replace(/"/g,'&quot;'))+'" onchange="_peRules['+i+'].action_value=this.value" style="'+s+';'+(needsFormula?'font-family:monospace;':'') +'" placeholder="'+(needsFormula?'{w} * 100':'v\u00e4rde')+'">'
                +'</div>'
                +(isProductAction ? '' : '<div style="margin-top:4px;display:grid;grid-template-columns:28px 1fr;gap:4px;align-items:center"><span style="font-size:10px;font-weight:700;color:#64748b">P\u00c5</span><select onchange="_peRules['+i+'].tillval_id=this.value" style="'+s+'"><option value="">\u2014 egen produkt \u2014</option>'+tvOpts+'</select></div>')
                +'<div style="margin-top:4px;padding-left:32px"><input value="'+((r.label||'').replace(/"/g,'&quot;'))+'" onchange="_peRules['+i+'].label=this.value" style="'+s+';width:100%;color:#94a3b8;font-style:italic" placeholder="Notering..."></div>'
                +'</div>';
        }).join('');
    }

    // Hjälptext med tillgängliga fält
    let helpFields = fields.length ? fields.map(f => '<code style="background:#e0f2fe;padding:1px 4px;border-radius:3px;font-size:10px">{'+f+'}</code>').join(' ') : '<em>Lägg till varianter först</em>';

    el.innerHTML = html
        +'<div style="padding:8px;border-top:1px solid #e5e7eb;font-size:10px;color:#94a3b8;line-height:1.6">'
        +'<strong>Formler:</strong> Använd '+helpFields+' i värdefält. '
        +'Exempel: <code style="background:#f1f5f9;padding:1px 4px;border-radius:3px;font-size:10px">Math.floor({bredd} / 40)</code> · '
        +'<code style="background:#f1f5f9;padding:1px 4px;border-radius:3px;font-size:10px">{pris_per_m2} * {area}</code> · '
        +'<code style="background:#f1f5f9;padding:1px 4px;border-radius:3px;font-size:10px">{totalt} * 1.2</code>'
        +'</div>';
}

function peAddRule() {
    _peRules.push({ field:'', operator:'*', value:'', tillval_id:'', action:'satt_pris', action_value:'', label:'', chain_op:'=' });
    peRenderRules();
}

// Utvärdera en regel-formel med variant-data
function peEvalFormula(formula, variantData) {
    if(!formula) return 0;
    // Om rent nummer
    const num = parseFloat(formula);
    if(!isNaN(num) && String(num) === String(formula).trim()) return num;
    // Ersätt {fält} med värden
    let expr = String(formula).replace(/\{(\w+)\}/g, (_, key) => {
        const val = variantData[key];
        return val !== undefined && val !== '' ? parseFloat(val) || 0 : 0;
    });
    try { return Function('"use strict"; return (' + expr + ')')(); }
    catch(e) { console.warn('Formelfel:', expr, e); return 0; }
}

// Kör alla regler mot en variant-rad och returnera beräknade tillval
function peApplyRules(rules, variantData, tillvalList) {
    const result = {};
    (rules || []).forEach(rule => {
        // Kolla villkor
        if(rule.operator !== '*' && rule.field) {
            const fieldVal = parseFloat(variantData[rule.field]) || 0;
            const ruleVal = parseFloat(rule.value) || 0;
            let match = false;
            switch(rule.operator) {
                case '==': match = fieldVal == ruleVal; break;
                case '!=': match = fieldVal != ruleVal; break;
                case '<': match = fieldVal < ruleVal; break;
                case '<=': match = fieldVal <= ruleVal; break;
                case '>': match = fieldVal > ruleVal; break;
                case '>=': match = fieldVal >= ruleVal; break;
            }
            if(!match) return;
        }
        const tv = rule.tillval_id || '__product';
        if(!result[tv]) result[tv] = { pris: null, antal: null, inkluderad: null, exkluderad: false };
        const val = peEvalFormula(rule.action_value, variantData);
        switch(rule.action) {
            case 'max': result[tv].max = val; break;
            case 'min': result[tv].min = val; break;
            case 'berakna_antal': result[tv].antal = val; break;
            case 'kraver': result[tv].inkluderad = true; result[tv].antal = result[tv].antal || 1; break;
            case 'exkluderar': result[tv].exkluderad = true; break;
            case 'inkluderar': result[tv].inkluderad = true; break;
            case 'satt_pris': result[tv].pris = val; break;
            case 'pris_tillagg': result[tv].pris = (result[tv].pris || 0) + val; break;
            case 'pris_per_enhet': result[tv].pris = val; break;
            case 'pris_multiplikator': result[tv].multiplikator = val; break;
            case 'satt_langd': { var op=rule.chain_op||'='; if(op==='+') result[tv].langd=(result[tv].langd||0)+val; else if(op==='-') result[tv].langd=(result[tv].langd||0)-val; else if(op==='*') result[tv].langd=(result[tv].langd||0)*val; else result[tv].langd=val; break; } case 'satt_bredd': { var op=rule.chain_op||'='; if(op==='+') result[tv].bredd=(result[tv].bredd||0)+val; else if(op==='-') result[tv].bredd=(result[tv].bredd||0)-val; else if(op==='*') result[tv].bredd=(result[tv].bredd||0)*val; else result[tv].bredd=val; break; } case 'satt_grundpris': result['__product'] = result['__product'] || {}; result['__product'].pris = val; break;
        }
    });
    return result;
}

// === DOKUMENT ===
let _peDocuments = [];

function peInitDocuments(documents) {
    _peDocuments = Array.isArray(documents) ? [...documents] : [];
}

function peRenderDocuments() {
    const el = document.getElementById('peDocumentsList');
    if(!el) return;
    if(!_peDocuments.length) { el.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8;font-size:12px">Inga dokument uppladdade.</div>'; return; }
    const typeLabels = { manual:'Manual', datablad:'Datablad', certifikat:'Certifikat', ovrigt:'Övrigt' };
    el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px">'
        +'<thead><tr style="background:#f8f9fa;position:sticky;top:0"><th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Dokument</th><th style="padding:6px 8px;text-align:left;font-weight:600;color:#64748b">Typ</th><th style="padding:6px 8px;text-align:right;font-weight:600;color:#64748b">Storlek</th><th style="padding:6px 8px;width:60px"></th></tr></thead><tbody>'
        + _peDocuments.map((d, i) => {
            const sizeStr = d.size ? (d.size > 1048576 ? (d.size/1048576).toFixed(1)+' MB' : (d.size/1024).toFixed(0)+' KB') : '—';
            return '<tr style="border-top:1px solid #f1f5f9">'
                +'<td style="padding:6px 8px"><a href="'+d.file+'" target="_blank" style="color:#0284c7;text-decoration:none;font-weight:500">'+escHtml(d.name)+'</a></td>'
                +'<td style="padding:6px 8px;color:#64748b"><span style="background:#f1f5f9;padding:2px 8px;border-radius:4px;font-size:11px">'+(typeLabels[d.type]||d.type)+'</span></td>'
                +'<td style="padding:6px 8px;text-align:right;color:#94a3b8">'+sizeStr+'</td>'
                +'<td style="padding:6px 8px;text-align:center"><button onclick="peRemoveDocument('+i+')" style="background:none;border:none;color:#ef4444;cursor:pointer;font-size:14px" title="Ta bort">&times;</button></td>'
                +'</tr>';
        }).join('')
        +'</tbody></table>';
}

async function peUploadDocument(productId) {
    const input = document.getElementById('peDocInput');
    if(!input || !input.files.length) return;
    const file = input.files[0];
    const docType = document.getElementById('peDocType')?.value || 'manual';
    const fd = new FormData();
    fd.append('id', productId);
    fd.append('document', file);
    fd.append('doc_name', file.name.replace(/\.[^.]+$/, ''));
    fd.append('doc_type', docType);
    try {
        const res = await fetch('/api/products.php?upload=1&document=1', { method:'POST', body: fd });
        const data = await res.json();
        if(data.success && data.documents) {
            _peDocuments = data.documents;
            peRenderDocuments();
        } else {
            alert('Fel: ' + (data.error || 'okänt'));
        }
    } catch(e) { alert('Uppladdningsfel: ' + e.message); }
    input.value = '';
}

async function peRemoveDocument(idx) {
    const doc = _peDocuments[idx];
    if(!doc) return;
    try {
        const res = await fetch('/api/products.php?remove_document=1', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ id: _peProductId, doc_file: doc.file })
        });
        const data = await res.json();
        if(data.success) { _peDocuments = data.documents || []; peRenderDocuments(); }
    } catch(e) { console.error('Remove document error:', e); }
}

let _peCroppedFile = null;

function previewProductImage(input) {
    if(!input.files || !input.files[0]) return;
    const file = input.files[0];
    const reader = new FileReader();
    reader.onload = e => openImageCropper(e.target.result, file.name);
    reader.readAsDataURL(file);
}

function openImageCropper(dataUrl, fileName) {
    // Remove existing cropper
    document.getElementById('imageCropperModal')?.remove();

    const modal = document.createElement('div');
    modal.id = 'imageCropperModal';
    modal.style.cssText = 'position:fixed;inset:0;z-index:100001;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;font-family:Inter,sans-serif';

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;width:600px;max-width:95vw;overflow:hidden">'
        +'<div style="padding:16px 20px;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center">'
        +'<h3 style="margin:0;font-size:16px;font-weight:700;color:#1a1a1a">Beskär bild</h3>'
        +'<button onclick="document.getElementById(\'imageCropperModal\').remove()" style="background:none;border:none;font-size:22px;cursor:pointer;color:#94a3b8;line-height:1">&times;</button>'
        +'</div>'
        // Crop area
        +'<div style="position:relative;width:100%;aspect-ratio:16/9;overflow:hidden;background:#111;cursor:grab" id="cropContainer">'
        +'<img id="cropImage" src="'+dataUrl+'" style="position:absolute;transform-origin:0 0;user-select:none;-webkit-user-drag:none" draggable="false">'
        +'</div>'
        // Controls
        +'<div style="padding:16px 20px">'
        +'<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px">'
        +'<span style="font-size:12px;color:#64748b;min-width:50px">Zoom</span>'
        +'<input type="range" id="cropZoom" min="10" max="300" value="100" style="flex:1;accent-color:#024550" oninput="updateCropZoom(this.value)">'
        +'<span id="cropZoomLabel" style="font-size:12px;color:#64748b;min-width:40px;text-align:right">100%</span>'
        +'</div>'
        +'<div style="display:flex;gap:8px">'
        +'<button onclick="applyCrop()" style="flex:1;padding:10px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Använd beskärning</button>'
        +'<button onclick="applyCropFull()" style="padding:10px 16px;background:#f1f5f9;color:#334155;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Hela bilden</button>'
        +'<button onclick="document.getElementById(\'imageCropperModal\').remove()" style="padding:10px 16px;background:#fee2e2;color:#dc2626;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Avbryt</button>'
        +'</div>'
        +'</div>'
        +'</div>';

    document.body.appendChild(modal);

    // Init crop state
    const img = document.getElementById('cropImage');
    const container = document.getElementById('cropContainer');
    let scale = 1, posX = 0, posY = 0, dragging = false, startX = 0, startY = 0;

    img.onload = function() {
        // Fit image to cover container
        const cw = container.offsetWidth, ch = container.offsetHeight;
        const iw = img.naturalWidth, ih = img.naturalHeight;
        const coverScale = Math.max(cw / iw, ch / ih);
        scale = coverScale;
        posX = (cw - iw * scale) / 2;
        posY = (ch - ih * scale) / 2;
        img.style.transform = 'translate('+posX+'px,'+posY+'px) scale('+scale+')';
        document.getElementById('cropZoom').value = 100;
        document.getElementById('cropZoomLabel').textContent = '100%';
        // Store base scale
        img._baseScale = coverScale;
        img._scale = scale;
        img._posX = posX;
        img._posY = posY;
    };
    if(img.complete) img.onload();

    // Drag
    container.onmousedown = function(e) {
        dragging = true;
        startX = e.clientX - (img._posX || 0);
        startY = e.clientY - (img._posY || 0);
        container.style.cursor = 'grabbing';
        e.preventDefault();
    };
    document.addEventListener('mousemove', window._cropMouseMove = function(e) {
        if(!dragging) return;
        img._posX = e.clientX - startX;
        img._posY = e.clientY - startY;
        img.style.transform = 'translate('+img._posX+'px,'+img._posY+'px) scale('+(img._scale||1)+')';
    });
    document.addEventListener('mouseup', window._cropMouseUp = function() {
        dragging = false;
        if(container) container.style.cursor = 'grab';
    });

    // Touch support
    container.ontouchstart = function(e) {
        if(e.touches.length === 1) {
            dragging = true;
            startX = e.touches[0].clientX - (img._posX || 0);
            startY = e.touches[0].clientY - (img._posY || 0);
            e.preventDefault();
        }
    };
    container.ontouchmove = function(e) {
        if(!dragging || e.touches.length !== 1) return;
        img._posX = e.touches[0].clientX - startX;
        img._posY = e.touches[0].clientY - startY;
        img.style.transform = 'translate('+img._posX+'px,'+img._posY+'px) scale('+(img._scale||1)+')';
        e.preventDefault();
    };
    container.ontouchend = function() { dragging = false; };

    // Scroll wheel zoom
    container.onwheel = function(e) {
        e.preventDefault();
        const slider = document.getElementById('cropZoom');
        let v = parseInt(slider.value) + (e.deltaY < 0 ? 10 : -10);
        v = Math.max(10, Math.min(300, v));
        slider.value = v;
        updateCropZoom(v);
    };

    // Store filename for later
    img._fileName = fileName;
}

function updateCropZoom(val) {
    const img = document.getElementById('cropImage');
    if(!img || !img._baseScale) return;
    const pct = parseInt(val);
    img._scale = img._baseScale * (pct / 100);
    img.style.transform = 'translate('+(img._posX||0)+'px,'+(img._posY||0)+'px) scale('+img._scale+')';
    document.getElementById('cropZoomLabel').textContent = pct + '%';
}

function applyCrop() {
    const img = document.getElementById('cropImage');
    const container = document.getElementById('cropContainer');
    if(!img || !container) return;

    const cw = container.offsetWidth, ch = container.offsetHeight;
    const canvas = document.createElement('canvas');
    canvas.width = cw * 2;  // 2x for retina
    canvas.height = ch * 2;
    const ctx = canvas.getContext('2d');
    ctx.scale(2, 2);

    // Draw image with current transform
    const s = img._scale || 1;
    const px = img._posX || 0;
    const py = img._posY || 0;
    ctx.drawImage(img, px, py, img.naturalWidth * s, img.naturalHeight * s);

    canvas.toBlob(function(blob) {
        _peCroppedFile = new File([blob], (img._fileName || 'cropped.webp').replace(/\.[^.]+$/, '.webp'), { type: 'image/webp' });
        // Update preview
        const preview = document.getElementById('peImgPreview');
        if(preview) preview.innerHTML = '<img src="'+URL.createObjectURL(blob)+'" style="width:100%;height:100%;object-fit:cover">';
        document.getElementById('peUploadStatus').textContent = 'Beskuren bild (' + (blob.size/1024).toFixed(0) + ' KB) — sparas vid klick på Spara';
        // Cleanup
        document.removeEventListener('mousemove', window._cropMouseMove);
        document.removeEventListener('mouseup', window._cropMouseUp);
        document.getElementById('imageCropperModal')?.remove();
    }, 'image/webp', 0.9);
}

function applyCropFull() {
    const img = document.getElementById('cropImage');
    if(!img) return;

    // Use full original image, just convert to webp at original size
    const canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);

    canvas.toBlob(function(blob) {
        _peCroppedFile = new File([blob], (img._fileName || 'full.webp').replace(/\.[^.]+$/, '.webp'), { type: 'image/webp' });
        const preview = document.getElementById('peImgPreview');
        if(preview) preview.innerHTML = '<img src="'+URL.createObjectURL(blob)+'" style="width:100%;height:100%;object-fit:cover">';
        document.getElementById('peUploadStatus').textContent = 'Hela bilden (' + (blob.size/1024).toFixed(0) + ' KB) — sparas vid klick på Spara';
        document.removeEventListener('mousemove', window._cropMouseMove);
        document.removeEventListener('mouseup', window._cropMouseUp);
        document.getElementById('imageCropperModal')?.remove();
    }, 'image/webp', 0.9);
}

async function saveProduct(id) {
    const catSel = document.getElementById('peCat');
    const stockSel = document.getElementById('peStock');
    const catLabel = catSel.options[catSel.selectedIndex].text;
    const stockClass = stockSel.options[stockSel.selectedIndex].dataset.class;

    // Upload image first if cropped or selected
    const fileInput = document.getElementById('peFileInput');
    let imgPath = '';
    const imageFile = _peCroppedFile || (fileInput && fileInput.files && fileInput.files[0]);
    if(imageFile) {
        const fd = new FormData();
        fd.append('id', id);
        fd.append('image', imageFile);
        document.getElementById('peUploadStatus').textContent = 'Laddar upp bild...';
        try {
            const upRes = await fetch('/api/products.php?upload=1', { method:'POST', body: fd });
            const upData = await upRes.json();
            if(upData.success) {
                imgPath = upData.img;
                document.getElementById('peUploadStatus').textContent = 'Bild uppladdad!';
            } else {
                document.getElementById('peUploadStatus').textContent = 'Fel: ' + (upData.error || 'okänt');
                return;
            }
        } catch(e) {
            document.getElementById('peUploadStatus').textContent = 'Uppladdningsfel: ' + e.message;
            return;
        }
    }

    const body = {
        id: id,
        name: document.getElementById('peName').value,
        cat: catSel.value,
        cat_label: catLabel,
        description: document.getElementById('peDesc').value,
        price: parseFloat(document.getElementById('pePrice').value) || 0,
        cost_price: parseFloat(document.getElementById('peCostPrice').value) || null, cost_currency: document.getElementById('peCostCurrency')?.value || 'SEK',
        stock: stockSel.value,
        stock_class: stockClass,
        green_tech_eligible: document.getElementById('peGreenTech').checked ? 1 : 0,
        rot_eligible: document.getElementById('peRotEligible')?.checked ? 1 : 0,
        supplier_id: parseInt(document.getElementById('peSupplier')?.value) || null,
        unit: document.getElementById('peUnit')?.value || 'st',
        markup_type: document.getElementById('peMarkupType')?.value || 'percent',
        markup_value: parseFloat(document.getElementById('peMarkupValue')?.value) || 0,
        tillval_obligatorisk: document.getElementById('peTillvalOblig')?.checked ? 1 : 0,
        tillval_hidden: document.getElementById('peTillvalHidden')?.checked ? 1 : 0
    };
    if(imgPath) body.img = imgPath;
    // Spara varianter + attribut i specs
    const existingSpecs = catalogProducts.find(x=>x.id===id)?.specs || {};
    body.specs = {...existingSpecs};
    // Spara rätt specs-typ
    if(_peSpecsType === 'style_width_variants') {
        body.specs.type = 'style_width_variants';
        body.specs.styles = _peStyles;
        delete body.specs.variants;
    } else if(_peSpecsType === 'width_variants') {
        body.specs.type = 'width_variants';
        body.specs.variants = _peVariants;
    } else if(_peSpecsType === 'window_sizes') {
        body.specs.type = 'window_sizes';
        body.specs.sizes = _peSizes;
        body.specs.models = _peWinModels;
        body.specs.widths = _peSpecsMeta.widths;
        body.specs.heights = _peSpecsMeta.heights;
        body.specs.supplier = _peSpecsMeta.supplier;
        body.specs.model = _peSpecsMeta.model;
    } else if(_peVariants.length) body.specs.variants = _peVariants;
    if(_peAttributes.length) body.specs.attributes = _peAttributes.filter(a => a.key || a.value);
    else delete body.specs.attributes;
    if(_peRules.length) body.specs.rules = _peRules.filter(r => r.action && r.action_value);
    else delete body.specs.rules;
    // Spara tjänst- och tillbehörs-kopplingar (båda i product_services)
    const linkedSvc = _peServices.filter(s => s.linked).map(s => ({
        service_id: s.id,
        default_on: s.default_on ? 1 : 0,
        hidden: s.hidden ? 1 : 0,
        mandatory: s.mandatory ? 1 : 0,
        default_variant: s.default_variant || null,
        default_qty: s.default_qty === '' ? null : s.default_qty,
        min_qty: s.min_qty === '' ? null : s.min_qty,
        max_qty: s.max_qty === '' ? null : s.max_qty
    }));
    const linkedAcc = _peAccessories.filter(s => s.linked).map(s => ({
        service_id: s.id,
        default_on: s.default_on ? 1 : 0,
        hidden: s.hidden ? 1 : 0,
        mandatory: s.mandatory ? 1 : 0,
        default_variant: s.default_variant || null,
        default_qty: s.default_qty === '' ? null : s.default_qty,
        min_qty: s.min_qty === '' ? null : s.min_qty,
        max_qty: s.max_qty === '' ? null : s.max_qty
    }));
    body._services = [...linkedSvc, ...linkedAcc];

    try {
        const res = await fetch('/api/products.php', {
            method: 'POST',
            headers: {'Content-Type':'application/json'},
            body: JSON.stringify(body)
        });
        const data = await res.json();
        if(data.success) {
            document.getElementById('productEditModal')?.remove();
            await loadCatalogFromDB();
            filterCatalog();
        } else {
            alert('Fel: ' + (data.error || 'okänt'));
        }
    } catch(e) {
        alert('Fel: ' + e.message);
    }
}

async function deleteProduct(id) {
    if(!confirm('Ta bort produkt ' + id + '?')) return;
    try {
        const res = await fetch('/api/products.php?id=' + id, { method: 'DELETE' });
        const data = await res.json();
        if(data.success) {
            document.getElementById('productEditModal')?.remove();
            await loadCatalogFromDB();
            filterCatalog();
        }
    } catch(e) { alert('Fel: ' + e.message); }
}

function showAddProductModal() {
    const cats = [{v:'solceller',l:'Solceller'},{v:'batteri',l:'Batteri'},{v:'batteri_utbyggnad',l:'Batteri Utbyggnad'},{v:'laddbox',l:'Laddbox'},{v:'fonster',l:'Fönster'},{v:'dorrar',l:'Dörrar'},{v:'tak',l:'Tak'},{v:'varmepump',l:'Värmepump'},{v:'taktvatt',l:'Taktvätt'},{v:'isolering',l:'Isolering'},{v:'tjanster',l:'Tjänster'},{v:'tillbehor',l:'Tillbehör'}];
    const stocks = [{v:'I lager',c:'green'},{v:'Få kvar',c:'yellow'},{v:'Beställning',c:'blue'},{v:'Slut',c:'red'}];

    const modal = document.createElement('div');
    modal.id = 'productEditModal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px';
    modal.onclick = e => { if(e.target === modal) modal.remove(); };

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:600px;width:100%;max-height:90vh;overflow-y:auto;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        +'<div style="padding:20px 24px;border-bottom:1px solid #f1f5f9"><h2 style="font-size:18px;font-weight:700;margin:0">Ny produkt</h2></div>'
        +'<div style="padding:24px">'
        +'<div id="peImgPreview" style="width:100%;aspect-ratio:16/9;background:#f8f9fa;border-radius:10px;border:2px dashed #e5e7eb;display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:pointer;margin-bottom:8px" onclick="document.getElementById(\'peFileInput\').click()">'
        +'<div style="text-align:center;color:#94a3b8"><svg viewBox="0 0 24 24" style="width:40px;height:40px;stroke:#cbd5e1;fill:none;stroke-width:1.5;margin:0 auto 8px;display:block"><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><div style="font-size:13px">Klicka för att ladda upp bild</div></div>'
        +'</div>'
        +'<input type="file" id="peFileInput" accept="image/*" style="display:none" onchange="previewProductImage(this)">'
        +'<div id="peUploadStatus" style="font-size:11px;color:#94a3b8;margin-bottom:12px"></div>'
        +'<div style="display:grid;grid-template-columns:100px 1fr;gap:12px 16px;align-items:center">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">ID</label>'
        +'<input id="peId" placeholder="t.ex. SOL05" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Namn</label>'
        +'<input id="peName" placeholder="Produktnamn" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Kategori</label>'
        +'<select id="peCat" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'+cats.map(c=>'<option value="'+c.v+'">'+c.l+'</option>').join('')+'</select>'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Pris (kr)</label>'
        +'<input id="pePrice" type="number" placeholder="0" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Watt</label>'
        +'<input id="peWatt" type="number" placeholder="Solpaneler" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">kWh</label>'
        +'<input id="peKwh" type="number" step="0.1" placeholder="Batteri" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Lagerstatus</label>'
        +'<select id="peStock" style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit">'+stocks.map(s=>'<option value="'+s.v+'" data-class="'+s.c+'">'+s.v+'</option>').join('')+'</select>'
        +'<label style="font-size:12px;font-weight:600;color:#64748b">Beskrivning</label>'
        +'<textarea id="peDesc" rows="3" placeholder="Produktbeskrivning..." style="padding:10px 14px;border:1px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;resize:vertical"></textarea>'
        +'</div>'
        +'<div style="display:flex;gap:10px;margin-top:20px">'
        +'<button onclick="saveNewProduct()" style="flex:1;padding:12px;background:#024550;color:#fff;border:none;border-radius:10px;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit">Skapa produkt</button>'
        +'<button onclick="this.closest(\'#productEditModal\').remove()" style="padding:12px 20px;background:#f1f5f9;color:#64748b;border:none;border-radius:10px;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit">Avbryt</button>'
        +'</div></div></div>';

    document.body.appendChild(modal);
}

async function saveNewProduct() {
    const id = document.getElementById('peId').value.trim();
    if(!id) { alert('ID krävs'); return; }
    const name = document.getElementById('peName').value.trim();
    if(!name) { alert('Namn krävs'); return; }

    const catSel = document.getElementById('peCat');
    const stockSel = document.getElementById('peStock');

    // Create product first
    const body = {
        id: id,
        name: name,
        cat: catSel.value,
        cat_label: catSel.options[catSel.selectedIndex].text,
        description: document.getElementById('peDesc').value,
        price: parseFloat(document.getElementById('pePrice').value) || 0,
        stock: stockSel.value,
        stock_class: stockSel.options[stockSel.selectedIndex].dataset.class,
        watt: parseInt(document.getElementById('peWatt').value) || null,
        kwh_capacity: parseFloat(document.getElementById('peKwh').value) || null
    };

    try {
        const res = await fetch('/api/products.php', {
            method: 'POST',
            headers: {'Content-Type':'application/json'},
            body: JSON.stringify(body)
        });
        const data = await res.json();
        if(!data.success) { alert('Fel: ' + (data.error||'')); return; }

        // Upload image if selected
        const fileInput = document.getElementById('peFileInput');
        if(fileInput && fileInput.files && fileInput.files[0]) {
            const fd = new FormData();
            fd.append('id', id);
            fd.append('image', fileInput.files[0]);
            await fetch('/api/products.php?upload=1', { method:'POST', body: fd });
        }

        document.getElementById('productEditModal')?.remove();
        await loadCatalogFromDB();
        filterCatalog();
    } catch(e) { alert('Fel: ' + e.message); }
}