js/contractors.js

Code: DEV-AC8060E6 Size: 15.1 KB Lines: 191 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/contractors.js

Task / Comment

Open report form
async function loadUEPage() {
    try {
        var r = await fetch('api/deals.php?action=contractors&t='+Date.now());
        _ueData = await r.json();
        var specs = {};
        _ueData.forEach(function(c){ if(c.specialty) specs[c.specialty] = (specs[c.specialty]||0)+1; });
        var specSel = document.getElementById('ueSpecFilter');
        if (specSel) {
            specSel.innerHTML = '<option value="">Alla specialiteter</option>' + Object.keys(specs).sort().map(function(s){ return '<option value="'+s+'">'+s+' ('+specs[s]+')</option>'; }).join('');
        }
        var totalContacts = _ueData.reduce(function(a,c){ return a+c.contacts.length; },0);
        var totalActive = _ueData.reduce(function(a,c){ return a+(c.stats?c.stats.active_projects:0); },0);
        var cntEl = document.getElementById('ueCount');
        if(cntEl) cntEl.textContent = '('+_ueData.length+' f\u00f6retag, '+totalContacts+' kontakter, '+totalActive+' p\u00e5g\u00e5ende projekt)';
        filterUE();
    } catch(e) { console.error('loadUEPage error:', e); }
}
function filterUE() {
    if(!_ueData) return;
    var search = (document.getElementById('ueSearch')||{}).value||'';
    search = search.toLowerCase();
    var spec = (document.getElementById('ueSpecFilter')||{}).value||'';
    var filtered = _ueData.filter(function(c){
        if(spec && c.specialty !== spec) return false;
        if(!search) return true;
        if(c.name.toLowerCase().indexOf(search) >= 0) return true;
        if((c.specialty||'').toLowerCase().indexOf(search) >= 0) return true;
        return c.contacts.some(function(ct){
            return (ct.contact_name||'').toLowerCase().indexOf(search) >= 0
                || (ct.phone||'').indexOf(search) >= 0
                || (ct.email||'').toLowerCase().indexOf(search) >= 0;
        });
    });
    renderUECards(filtered);
}
function renderUECards(companies) {
    var container = document.getElementById('ueContainer');
    if(!container) return;
    if(!companies.length) { container.innerHTML = '<div style="text-align:center;color:#94a3b8;padding:40px;font-size:14px">Inga entrepren\u00f6rer hittades</div>'; return; }
    container.innerHTML = '<div style="display:flex;flex-direction:column;gap:12px">'
        + companies.map(function(c, idx){
            var specColor = '#64748b';
            if((c.specialty||'').indexOf('elektriker')>=0) specColor = '#eab308';
            else if((c.specialty||'').indexOf('tak')>=0 || (c.specialty||'').indexOf('sol')>=0) specColor = '#f97316';
            else if((c.specialty||'').indexOf('f\u00f6nster')>=0) specColor = '#3b82f6';
            else if((c.specialty||'').indexOf('pl\u00e5t')>=0) specColor = '#6b7280';
            else if((c.specialty||'').indexOf('st\u00e4llning')>=0) specColor = '#8b5cf6';
            var st = c.stats || {};
            var hasProjects = st.total_projects > 0;
            var safeName = c.name.replace(/'/g,"\\'").replace(/"/g,'&quot;');

            return '<div id="ue-card-'+idx+'" style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.06)">'
                // Header - klickbar
                +'<div style="padding:14px 20px;display:flex;align-items:center;justify-content:space-between;cursor:pointer" onclick="toggleUECard('+idx+',\''+safeName+'\')">'
                +'<div style="flex:1;min-width:0">'
                +'<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">'
                +'<div style="width:40px;height:40px;border-radius:10px;background:'+specColor+'15;color:'+specColor+';display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;flex-shrink:0">'+c.name.charAt(0)+'</div>'
                +'<div>'
                +'<div style="font-size:16px;font-weight:700;color:#1a1a1a">'+c.name+'</div>'
                +'<div style="display:flex;align-items:center;gap:8px;margin-top:2px">'
                +(c.specialty?'<span style="font-size:11px;font-weight:600;padding:2px 10px;border-radius:10px;background:'+specColor+'15;color:'+specColor+';border:1px solid '+specColor+'30">'+c.specialty+'</span>':'')
                +'<span style="font-size:11px;color:#64748b">'+c.contacts.length+' kontakter</span>'
                +'</div></div></div>'
                +'<div style="display:flex;align-items:center;gap:8px">'
                +(hasProjects?'<span style="font-size:11px;font-weight:700;padding:3px 10px;border-radius:10px;background:#16a34a;color:#fff">'+st.active_projects+' p\u00e5g\u00e5ende</span>':'')
                +(hasProjects?'<span style="font-size:10px;color:#64748b">'+st.total_projects+' tot \u00b7 '+st.completed_projects+' klara \u00b7 '+st.cancelled_projects+' avbr</span>':'<span style="font-size:10px;color:#94a3b8">Inga projekt</span>')
                +'</div></div>'
                +'<svg id="ue-arrow-'+idx+'" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" style="flex-shrink:0;transition:transform .2s"><polyline points="6 9 12 15 18 9"/></svg>'
                +'</div>'
                // Expanderbar body
                +'<div id="ue-body-'+idx+'" style="max-height:0;overflow:hidden;transition:max-height .4s ease">'
                +'<div style="border-top:1px solid #e5e7eb">'
                // Kontakter
                +'<div style="padding:12px 20px">'
                +'<div style="font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">Kontakter</div>'
                +'<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:8px">'
                + c.contacts.map(function(ct){
                    return '<div style="display:flex;align-items:center;gap:10px;padding:8px 12px;background:#f8f9fa;border-radius:8px">'
                        +'<div style="width:32px;height:32px;border-radius:50%;background:#e2e8f0;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#64748b;flex-shrink:0">'+((ct.contact_name||'?').charAt(0))+'</div>'
                        +'<div style="flex:1;min-width:0">'
                        +'<div style="font-size:13px;font-weight:600;color:#334155">'+(ct.contact_name||'Ok\u00e4nd')+'</div>'
                        +(ct.contact_title?'<div style="font-size:11px;color:#94a3b8">'+ct.contact_title+'</div>':'')
                        +'</div>'
                        +'<div style="display:flex;gap:4px;flex-shrink:0">'
                        +(ct.phone?'<a href="tel:'+ct.phone+'" onclick="event.stopPropagation()" style="display:flex;align-items:center;gap:3px;padding:3px 8px;border-radius:6px;background:#f0fdf4;color:#16a34a;text-decoration:none;font-size:11px;font-weight:600;border:1px solid #86efac"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>'+ct.phone+'</a>':'')
                        +(ct.email?'<a href="mailto:'+ct.email+'" onclick="event.stopPropagation()" style="display:flex;align-items:center;gap:3px;padding:3px 8px;border-radius:6px;background:#eff6ff;color:#3b82f6;text-decoration:none;font-size:11px;font-weight:600;border:1px solid #93c5fd"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>'+ct.email+'</a>':'')
                        +'</div></div>';
                }).join('')
                +'</div></div>'
                // Projekt-sektion (laddas vid expand)
                +'<div id="ue-projects-'+idx+'" style="padding:0 20px 16px">'
                +'<div style="text-align:center;padding:20px;color:#94a3b8;font-size:13px">Laddar projekt...</div>'
                +'</div>'
                +'</div></div></div>';
        }).join('')
        +'</div>';
}

var _ueExpandedProjects = {};
async function toggleUECard(idx, ueName) {
    var body = document.getElementById('ue-body-'+idx);
    var arrow = document.getElementById('ue-arrow-'+idx);
    if (!body) return;
    var isOpen = body.style.maxHeight && body.style.maxHeight !== '0px';
    if (isOpen) {
        body.style.maxHeight = '0px';
        if(arrow) arrow.style.transform = 'rotate(0deg)';
    } else {
        body.style.maxHeight = '2000px';
        if(arrow) arrow.style.transform = 'rotate(180deg)';
        // Ladda projekt om inte redan laddat
        if (!_ueExpandedProjects[ueName]) {
            _ueExpandedProjects[ueName] = true;
            loadUECardProjects(idx, ueName);
        }
    }
}

async function loadUECardProjects(idx, ueName) {
    var container = document.getElementById('ue-projects-'+idx);
    if (!container) return;
    try {
        var r = await fetch('api/deals.php?action=ue_projects&ue='+encodeURIComponent(ueName)+'&t='+Date.now());
        var projects = await r.json();
        if (projects.error) { container.innerHTML = '<div style="color:#ef4444;padding:8px;font-size:13px">'+projects.error+'</div>'; return; }
        if (!projects.length) { container.innerHTML = '<div style="padding:12px 0;color:#94a3b8;font-size:13px">Inga projekt hittade f\u00f6r denna UE</div>'; return; }

        var activeStatuses = ['Att best\u00e4lla','Att boka','Att projektera','Bokat','Leveransbevakning','Ny order','Projektering bokad','P\u00e5g\u00e5ende','Support / Fels\u00f6kning'];
        var active = projects.filter(function(p){ return activeStatuses.indexOf(p.group_title)>=0; });
        var completed = projects.filter(function(p){ return p.group_title==='F\u00e4rdigst\u00e4llt'; });
        var cancelled = projects.filter(function(p){ return ['Avbrott','Avbrott f\u00e4rdigst\u00e4llt','Avbrott ny order / projektering','\u00c5nger'].indexOf(p.group_title)>=0; });
        var other = projects.filter(function(p){ return active.indexOf(p)<0 && completed.indexOf(p)<0 && cancelled.indexOf(p)<0; });
        if(other.length) active = active.concat(other);

        function statusColor(gt) {
            if(activeStatuses.indexOf(gt)>=0) return '#16a34a';
            if(gt==='F\u00e4rdigst\u00e4llt') return '#3b82f6';
            return '#ef4444';
        }

        function projSection(rows, label, color, defaultOpen) {
            if(!rows.length) return '';
            var totalVal = rows.reduce(function(a,p){ return a+parseFloat(p.ordervarde_ink_moms||0); },0);
            return '<div style="margin-bottom:12px">'
                +'<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;cursor:pointer" onclick="var t=this.nextElementSibling;t.style.display=t.style.display===\'none\'?\'block\':\'none\';this.querySelector(\'svg\').style.transform=t.style.display===\'none\'?\'rotate(-90deg)\':\'rotate(0deg)\'">'
                +'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="'+color+'" stroke-width="2.5" style="transition:transform .2s;transform:'+(defaultOpen?'rotate(0deg)':'rotate(-90deg)')+'"><polyline points="6 9 12 15 18 9"/></svg>'
                +'<span style="font-size:12px;font-weight:700;color:'+color+'">'+label+'</span>'
                +'<span style="font-size:11px;font-weight:600;background:'+color+';color:#fff;padding:1px 8px;border-radius:10px">'+rows.length+'</span>'
                +(totalVal?'<span style="font-size:11px;color:#64748b;margin-left:auto">'+fmtKr(totalVal)+'</span>':'')
                +'</div>'
                +'<div style="display:'+(defaultOpen?'block':'none')+'">'
                +'<table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#f8f9fa">'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Aff\u00e4rsnr</th>'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Kund</th>'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Adress</th>'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Status</th>'
                +'<th style="padding:6px 8px;text-align:right;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Orderv\u00e4rde</th>'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Region</th>'
                +'<th style="padding:6px 8px;text-align:left;border-bottom:1px solid #e5e7eb;font-weight:600;color:#64748b">Datum</th>'
                +'</tr></thead><tbody>'
                +rows.map(function(p){
                    var sc = statusColor(p.group_title);
                    return '<tr style="cursor:pointer;transition:background .1s" onmouseover="this.style.background=\'#f8fafc\'" onmouseout="this.style.background=\'#fff\'" onclick="'+(p.deal_id?'showDealDetail('+p.deal_id+')':'')+'">'
                        +'<td style="padding:6px 8px;font-weight:600;color:#024550;border-bottom:1px solid #f1f5f9">'+(p.deal_number||'-')+'</td>'
                        +'<td style="padding:6px 8px;border-bottom:1px solid #f1f5f9">'+(p.customer_name||'-')+'</td>'
                        +'<td style="padding:6px 8px;border-bottom:1px solid #f1f5f9;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+(p.property_address||'-')+'</td>'
                        +'<td style="padding:6px 8px;border-bottom:1px solid #f1f5f9"><span style="font-size:10px;padding:2px 8px;border-radius:8px;color:#fff;background:'+sc+';white-space:nowrap">'+(p.group_title||'-')+'</span></td>'
                        +'<td style="padding:6px 8px;text-align:right;border-bottom:1px solid #f1f5f9;font-weight:600">'+(p.ordervarde_ink_moms?fmtKr(parseFloat(p.ordervarde_ink_moms)):'-')+'</td>'
                        +'<td style="padding:6px 8px;border-bottom:1px solid #f1f5f9">'+(p.region||'-')+'</td>'
                        +'<td style="padding:6px 8px;border-bottom:1px solid #f1f5f9;color:#64748b">'+(p.datum_salj||'-')+'</td>'
                        +'</tr>';
                }).join('')
                +'</tbody></table></div></div>';
        }

        container.innerHTML = '<div style="font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px;padding-top:4px;border-top:1px solid #f1f5f9">Projekt ('+projects.length+')</div>'
            + projSection(active, 'P\u00e5g\u00e5ende', '#16a34a', true)
            + projSection(completed, 'F\u00e4rdigst\u00e4llda', '#3b82f6', false)
            + projSection(cancelled, 'Avbrutna', '#ef4444', false);

        // Uppdatera max-height efter innehåll laddats
        var body = document.getElementById('ue-body-'+idx);
        if(body) body.style.maxHeight = body.scrollHeight + 'px';
    } catch(e) {
        container.innerHTML = '<div style="color:#ef4444;padding:8px;font-size:13px">Fel vid laddning: '+e.message+'</div>';
    }
}

// ============ INKÖP & LEVERANTÖRER ============