async function loadDealPipeline() {
try {
var params = 'action=pipeline&t='+Date.now();
var dateFrom = document.getElementById('pipelineDateFrom');
var dateTo = document.getElementById('pipelineDateTo');
var region = document.getElementById('pipelineRegion');
if(dateFrom && dateFrom.value) params += '&date_from='+dateFrom.value;
if(dateTo && dateTo.value) params += '&date_to='+dateTo.value;
if(region && region.value) params += '®ion='+encodeURIComponent(region.value);
var ue = document.getElementById('pipelineUE');
if(ue && ue.value) params += '&ue='+encodeURIComponent(ue.value);
var prod = document.getElementById('pipelineProduct');
if(prod && prod.value) params += '&product_type='+encodeURIComponent(prod.value);
const r = await fetch('api/deals.php?'+params);
const data = await r.json();
renderPipeline(data.pipeline);
// Populate region dropdown on first load
if(region && region.options.length <= 1){
fetch('api/deals.php?action=pipeline&t=0').then(function(r){return r.json();}).then(function(allData){
var regions = {};
var ues = {};
Object.values(allData.pipeline).flat().forEach(function(d){
if(d.region) regions[d.region] = true;
if(d.tilldelad_ue){
d.tilldelad_ue.split(',').forEach(function(u){
u = u.trim();
if(!u || u.indexOf('@') >= 0) return;
// Skip likely person names (two words, no company keywords)
var companyWords = /AB$|AB |Bygg|Tak|El[tj]|Sol[aeo]|Kyl|Plåt|Iso|Move|Clean|Nordic|Reko|Tech|Vänner|service|maskin|Entreprenad|Energi|Koppar|Furuviksplåt|Powermove|Solartech|Algust|Inovela|WBOAR|ZLATAN|DAKERT|HemsAB|TFM/i;
if(!companyWords.test(u)) return;
ues[u] = (ues[u]||0) + 1;
});
}
});
Object.keys(regions).sort().forEach(function(rg){
var opt = document.createElement('option');
opt.value = rg; opt.textContent = rg;
region.appendChild(opt);
});
var ueSel = document.getElementById('pipelineUE');
if(ueSel){
Object.keys(ues).sort().forEach(function(u){
var opt = document.createElement('option');
opt.value = u; opt.textContent = u + ' (' + ues[u] + ')';
ueSel.appendChild(opt);
});
}
var prods = {};
var prodLabels = typeof PRODUCT_LABELS !== 'undefined' ? PRODUCT_LABELS : {};
Object.values(allData.pipeline).flat().forEach(function(d){
if(d.product_types){
d.product_types.split(',').forEach(function(pt){
pt = pt.trim();
if(pt) prods[pt] = true;
});
}
});
var prodSel = document.getElementById('pipelineProduct');
if(prodSel){
Object.keys(prods).sort().forEach(function(pt){
var opt = document.createElement('option');
opt.value = pt;
opt.textContent = prodLabels[pt] || pt.charAt(0).toUpperCase() + pt.slice(1);
prodSel.appendChild(opt);
});
}
}).catch(function(){});
}
} catch(e) { console.error('loadDealPipeline error:', e); }
}
function clearPipelineFilters(){
document.getElementById('pipelineDateFrom').value = '';
document.getElementById('pipelineDateTo').value = '';
document.getElementById('pipelineRegion').value = '';
document.getElementById('pipelineUE').value = '';
document.getElementById('pipelineProduct').value = '';
_akutFilterActive = false;
_avbrottFilterActive = false;
_colAkutFilter = {};
var akutBox = document.getElementById('pipelineAkut');
akutBox.style.border = '1.5px solid #f59e0b';
akutBox.style.background = '#fef9c3';
var avbBox = document.getElementById('pipelineAvbrott');
avbBox.style.border = '1.5px solid #fca5a5';
avbBox.style.background = '#fef2f2';
loadDealPipeline();
}
var _pipelineCollapsed = {};
var _pipelineData = {};
var _akutFilterActive = false;
var _colAkutFilter = {};
var _avbrottFilterActive = false;
function toggleAkutExpand() {
_akutFilterActive = !_akutFilterActive;
var box = document.getElementById('pipelineAkut');
if(_akutFilterActive){
box.style.border = '2.5px solid #ef4444';
box.style.background = '#fef2f2';
} else {
box.style.border = '1.5px solid #f59e0b';
box.style.background = '#fef9c3';
}
renderPipeline(_pipelineData);
}
function toggleAvbrottExpand() {
_avbrottFilterActive = !_avbrottFilterActive;
var box = document.getElementById('pipelineAvbrott');
if(_avbrottFilterActive){
box.style.border = '2.5px solid #ef4444';
box.style.background = '#fecaca';
} else {
box.style.border = '1.5px solid #fca5a5';
box.style.background = '#fef2f2';
}
renderPipeline(_pipelineData);
}
function togglePipelineCol(stage) {
var cur = (stage in _pipelineCollapsed) ? _pipelineCollapsed[stage] : true;
_pipelineCollapsed[stage] = !cur;
renderPipeline(_pipelineData);
}
function toggleColAkut(stage, event) {
event.stopPropagation();
_colAkutFilter[stage] = !_colAkutFilter[stage];
renderPipeline(_pipelineData);
}
function pipelineColDragEnter(el, stage) {
el.style.opacity = '0.7';
clearTimeout(window._colExpandTimer);
window._colExpandTimer = setTimeout(function(){
_pipelineCollapsed[stage] = false;
renderPipeline(_pipelineData);
}, 500);
}
function renderPipeline(pipeline) {
_pipelineData = pipeline;
const stages = ['projektering','bestallning','leverans','montering','besiktning','fardigstall'];
const container = document.getElementById('pipelineColumns');
// Avbrott section (top-right)
const avbrott = [...(pipeline['avbrott']||[]), ...(pipeline['anger']||[]), ...(pipeline['ej_godkand']||[])];
document.getElementById('avbrottBadge').textContent = avbrott.length;
const avbrottTotal = avbrott.reduce((a,d) => a + parseFloat(d.ordervarde_ink_moms||0), 0);
document.getElementById('avbrottSum').textContent = avbrottTotal ? fmtKr(avbrottTotal) : '';
// Akut section - deals older than 30 days (not fardigstall/avbrott/anger/ej_godkand)
const now = Date.now();
const akutDeals = [];
['projektering','bestallning','leverans','montering','besiktning'].forEach(function(s){
(pipeline[s]||[]).forEach(function(d){
if(d.datum_salj && (now - new Date(d.datum_salj).getTime())/864e5 > 30) akutDeals.push(Object.assign({_stage:s},d));
});
});
document.getElementById('akutBadge').textContent = akutDeals.length;
var akutTotal = akutDeals.reduce(function(a,d){return a+parseFloat(d.ordervarde_ink_moms||0);},0);
document.getElementById('akutSum').textContent = akutTotal ? fmtKr(akutTotal) : '';
document.getElementById('pipelineAkut').style.animation = akutDeals.length > 0 ? 'blink-red 2s ease-in-out infinite' : 'none';
document.getElementById('akutList').innerHTML = akutDeals.length ? akutDeals.map(function(d){
var days = Math.floor((now - new Date(d.datum_salj).getTime())/864e5);
var stageLabel = DEAL_STATUS_LABELS[d._stage]||d._stage;
return '<div onclick="event.stopPropagation();showDealDetail('+d.id+')" style="background:#fff;border:1px solid #f59e0b;border-radius:6px;padding:6px 8px;margin-bottom:4px;cursor:pointer;font-size:11px">'
+'<div style="display:flex;justify-content:space-between"><span style="font-weight:600;color:#1a1a1a">'+(d.customer_name||'Okänd')+'</span><span style="color:#ef4444;font-weight:700;font-size:10px">'+days+' dagar</span></div>'
+'<div style="color:#64748b">'+(d.deal_number||'')+'</div>'
+'<div style="display:flex;justify-content:space-between;margin-top:2px"><span style="font-size:10px;color:#b45309">'+stageLabel+'</span>'+(d.ordervarde_ink_moms?'<span style="color:#024550;font-weight:600">'+fmtKr(parseFloat(d.ordervarde_ink_moms))+'</span>':'')+'</div>'
+'</div>';
}).join('') : '<div style="font-size:11px;color:#94a3b8;text-align:center;padding:8px">Inga akuta</div>';
document.getElementById('avbrottList').innerHTML = avbrott.length ? avbrott.map(d => {
return '<div onclick="event.stopPropagation();showDealDetail('+d.id+')" style="background:#fff;border:1px solid #fca5a5;border-radius:6px;padding:6px 8px;margin-bottom:4px;cursor:pointer;font-size:11px">'
+'<div style="font-weight:600;color:#1a1a1a">'+(d.customer_name||'Okänd')+'</div>'
+'<div style="color:#64748b">'+(d.deal_number||'')+'</div>'
+(d.avbrott_status?'<div style="color:#ef4444;font-weight:500;margin-top:2px">'+d.avbrott_status+'</div>':'')
+(d.ordervarde_ink_moms?'<div style="color:#024550;font-weight:600;margin-top:1px">'+fmtKr(parseFloat(d.ordervarde_ink_moms))+'</div>':'')
+'</div>';
}).join('') : '<div style="font-size:11px;color:#94a3b8;text-align:center;padding:8px">Inga avbrott</div>';
container.innerHTML = stages.map(s => {
var deals = pipeline[s] || [];
if(_avbrottFilterActive){
deals = [];
} else if(_akutFilterActive && s !== 'fardigstall'){
deals = deals.filter(function(d){ return d.datum_salj && (Date.now() - new Date(d.datum_salj).getTime())/864e5 > 30; });
} else if(_akutFilterActive && s === 'fardigstall'){
deals = [];
} else if(_colAkutFilter[s] && s !== 'fardigstall'){
deals = deals.filter(function(d){ return d.datum_salj && (Date.now() - new Date(d.datum_salj).getTime())/864e5 > 30; });
}
const color = DEAL_STATUS_COLORS[s];
const total = deals.reduce((a,d) => a + parseFloat(d.ordervarde_ink_moms||0), 0);
const collapsed = (s in _pipelineCollapsed) ? _pipelineCollapsed[s] : (deals.length === 0);
if (collapsed) {
return '<div style="min-width:44px;width:44px;cursor:pointer;user-select:none" onclick="togglePipelineCol(\''+s+'\')" ondragover="pipelineDragOver(event)" ondragenter="pipelineColDragEnter(this,\''+s+'\')" ondragleave="this.style.opacity=\'1\'" ondrop="pipelineDrop(event,\''+s+'\')">'
+'<div style="padding:8px 4px;background:'+color+'15;border-radius:10px;border-left:3px solid '+color+';height:100%;display:flex;flex-direction:column;align-items:center;gap:8px">'
+'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="'+color+'" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>'
+'<span style="writing-mode:vertical-lr;text-orientation:mixed;font-size:11px;font-weight:700;color:'+color+'">'+DEAL_STATUS_LABELS[s]+'</span>'
+'<span style="font-size:11px;font-weight:700;background:'+color+';color:#fff;padding:2px 6px;border-radius:8px">'+deals.length+'</span>'
+(total?'<span style="writing-mode:vertical-lr;text-orientation:mixed;font-size:9px;color:#64748b">'+fmtKr(total)+'</span>':'')
+'</div></div>';
}
return '<div style="min-width:155px;flex:1" data-stage="'+s+'">'
+'<div style="padding:6px 10px;background:'+color+'15;border-radius:10px 10px 0 0;border-bottom:3px solid '+color+';user-select:none">'
+'<div style="display:flex;justify-content:space-between;align-items:center">'
+'<div style="display:flex;align-items:center;gap:4px;cursor:pointer" onclick="togglePipelineCol(\''+s+'\')">'
+'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="'+color+'" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg>'
+'<span style="font-size:11px;font-weight:700;color:'+color+'">'+DEAL_STATUS_LABELS[s]+'</span></div>'
+'<div style="display:flex;align-items:center;gap:4px">'
+(function(){var ac=0;if(s!=='fardigstall'){(pipeline[s]||[]).forEach(function(d){if(d.datum_salj&&(Date.now()-new Date(d.datum_salj).getTime())/864e5>30)ac++;});}return ac?'<span onclick="toggleColAkut(\''+s+'\',event)" style="font-size:9px;font-weight:700;background:'+(_colAkutFilter[s]?'#b91c1c':'#ef4444')+';color:#fff;padding:1px 5px;border-radius:8px;cursor:pointer;animation:blink-red 1.5s ease-in-out infinite;'+(_colAkutFilter[s]?'outline:2px solid #7f1d1d;':'')+'" title="Visa bara akuta">🔥'+ac+'</span>':'';}())
+'<span style="font-size:10px;font-weight:600;background:'+color+';color:#fff;padding:1px 7px;border-radius:10px">'+deals.length+'</span>'
+'</div></div>'
+'<div style="font-size:9px;color:#64748b;margin-top:1px;padding-left:18px">'+(total?fmtKr(total):'')+'</div></div>'
+'<div class="pipeline-drop" data-stage="'+s+'" ondragover="pipelineDragOver(event)" ondrop="pipelineDrop(event,\''+s+'\')" ondragenter="this.style.background=\''+color+'10\'" ondragleave="this.style.background=\'#f8f9fa\'" style="background:#f8f9fa;border-radius:0 0 10px 10px;padding:4px;min-height:80px;display:flex;flex-direction:column;gap:4px;transition:background .15s">'
+(deals.length ? deals.map(d => pipelineCard(d)).join('') : '<div style="text-align:center;color:#cbd5e1;font-size:11px;padding:16px 0">Inga projekt</div>')
+'</div></div>';
}).join('');
// If avbrott filter active, append avbrott columns
if(_avbrottFilterActive){
var avbrottGroups = {'avbrott':[],'anger':[],'ej_godkand':[]};
['avbrott','anger','ej_godkand'].forEach(function(s){
(pipeline[s]||[]).forEach(function(d){ avbrottGroups[s].push(d); });
});
var abLabels = {avbrott:'Avbrott',anger:'Ånger',ej_godkand:'Ej godkänd'};
var abColors = {avbrott:'#94a3b8',anger:'#ef4444',ej_godkand:'#dc2626'};
container.innerHTML = ['avbrott','anger','ej_godkand'].map(function(s){
var ds = avbrottGroups[s];
var cl = abColors[s];
var tot = ds.reduce(function(a,d){return a+parseFloat(d.ordervarde_ink_moms||0);},0);
return '<div style="min-width:200px;flex:1" data-stage="'+s+'">'
+'<div style="padding:6px 10px;background:'+cl+'15;border-radius:10px 10px 0 0;border-bottom:3px solid '+cl+'">'
+'<div style="display:flex;justify-content:space-between;align-items:center">'
+'<span style="font-size:11px;font-weight:700;color:'+cl+'">'+abLabels[s]+'</span>'
+'<span style="font-size:10px;font-weight:600;background:'+cl+';color:#fff;padding:1px 7px;border-radius:10px">'+ds.length+'</span></div>'
+'<div style="font-size:9px;color:#64748b;margin-top:1px">'+(tot?fmtKr(tot):'')+'</div></div>'
+'<div style="background:#f8f9fa;border-radius:0 0 10px 10px;padding:4px;min-height:80px;display:flex;flex-direction:column;gap:4px">'
+(ds.length?ds.map(function(d){return pipelineCard(d);}).join(''):'<div style="text-align:center;color:#cbd5e1;font-size:11px;padding:16px 0">Inga</div>')
+'</div></div>';
}).join('');
}
}
function pipelineCard(d) {
const products = (d.product_types||'').split(',').filter(Boolean).map(p => PRODUCT_LABELS[p]||p).join(', ');
return '<div draggable="true" ondragstart="pipelineDragStart(event,'+d.id+')" onclick="showDealDetail('+d.id+')" data-deal-id="'+d.id+'" style="background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:7px 8px;cursor:grab;transition:box-shadow .15s,opacity .15s" onmouseover="this.style.boxShadow=\'0 2px 8px rgba(0,0,0,.08)\'" onmouseout="this.style.boxShadow=\'none\'">'
+(function(){var _isAkut=d.datum_salj&&d.status!=='fardigstall'&&(Date.now()-new Date(d.datum_salj).getTime())/864e5>30;return _isAkut?'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px"><span style="font-size:11px;font-weight:600;color:#1a1a1a;line-height:1.2">'+(d.customer_name||'Okänd kund')+'</span><span style="font-size:12px;animation:blink-red 1.5s ease-in-out infinite" title="Akut - äldre än 30 dagar">🔥</span></div>':'<div style="font-size:11px;font-weight:600;color:#1a1a1a;margin-bottom:2px;line-height:1.2">'+(d.customer_name||'Okänd kund')+'</div>';}())
+'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px"><span style="font-size:10px;color:#64748b">'+(d.deal_number||'')+'</span>'+(d.datum_salj?(function(){var age=(Date.now()-new Date(d.datum_salj).getTime())/864e5;var old=age>30&&d.status!=='fardigstall';return '<span style="font-size:9px;font-weight:'+(old?'700':'400')+';color:'+(old?'#ef4444':'#94a3b8')+';'+(old?'animation:blink-red 1.5s ease-in-out infinite':'')+'">'+ d.datum_salj+'</span>'})():'')+'</div>'
+(products?'<div style="font-size:9px;color:#94a3b8;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+products+'</div>':'')
+(d.ordervarde_ink_moms?'<div style="font-size:11px;font-weight:700;color:#024550">'+fmtKr(parseFloat(d.ordervarde_ink_moms))+'</div>':'')
+'<div style="display:flex;gap:3px;flex-wrap:wrap;margin-top:2px;align-items:center">'
+(d.salj_status?'<span style="font-size:8px;padding:0px 5px;border-radius:3px;font-weight:600;'+(({'godkand':'background:#dcfce7;color:#166534','anger':'background:#fee2e2;color:#991b1b','inte_godkand':'background:#fee2e2;color:#991b1b','raddad_anger':'background:#f3e8ff;color:#7c3aed','anger_fraga':'background:#e0f2fe;color:#0369a1','ej_hanterad':'background:#f1f5f9;color:#64748b','avvakta':'background:#fef9c3;color:#854d0e'})[d.salj_status]||'background:#f1f5f9;color:#64748b')+'">'+(({'godkand':'Godkänd','anger':'Ånger','inte_godkand':'Ej godkänd','raddad_anger':'Räddad ånger','anger_fraga':'Ånger?','ej_hanterad':'Ej hanterad','avvakta':'Avvakta','underpris':'Underpris','halv_provis':'Halv provision','ingen_provis':'Ingen provision','senare_loneunderlag':'Senare löneunderlag'})[d.salj_status]||d.salj_status)+'</span>':'')
+(d.saljare1_name?'<span style="font-size:9px;color:#94a3b8">'+d.saljare1_name+'</span>':'')
+(d.region?'<span style="font-size:8px;background:#dbeafe;color:#1e40af;padding:0px 5px;border-radius:3px">'+d.region+'</span>':'')
+'</div>'
+(d.tilldelad_ue?'<div style="font-size:8px;color:#9a3412;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="'+d.tilldelad_ue+'">UE: '+d.tilldelad_ue+'</div>':'')
+'</div>';
}
var _dragDealId = null;
function pipelineDragStart(e, dealId) {
_dragDealId = dealId;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', dealId);
e.target.style.opacity = '0.5';
setTimeout(() => { if(e.target) e.target.style.opacity = '1'; }, 300);
}
function pipelineDragOver(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }
async function pipelineDrop(e, newStage) {
e.preventDefault();
e.currentTarget.style.background = '#f8f9fa';
if (!_dragDealId) return;
const dealId = _dragDealId;
_dragDealId = null;
try {
const r = await fetch('api/deals.php?action=status', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({deal_id: dealId, status: newStage, staff_id: gStaffId})
});
const res = await r.json();
if (res.success) loadDealPipeline();
else alert(res.error || 'Kunde inte flytta');
} catch(err) { console.error('Drag drop error:', err); }
}