// projekt.js - Deals, pipeline, filters
// Returnerar URL-parameter '&saljare_id=X' om användaren är begränsad till egna affärer.
function _dealsScopeParam(){
var scope = (typeof getRoleSetting === 'function') ? getRoleSetting('projekt.scope') : null;
if (!scope) {
var role = (window.gUserRole || sessionStorage.getItem('gUserRole') || '');
scope = (['admin','systemadmin','saljchef','ekonomi'].indexOf(role) >= 0) ? 'all' : 'own';
}
if (scope === 'all') return '';
var sid = parseInt(window.gStaffId || sessionStorage.getItem('gStaffId') || 0, 10);
return sid ? ('&saljare_id=' + sid) : '';
}
function setDealView(v) {
dealView = v;
loadDeals();
}
var dealsLoaded = false;
var allDealsCache = [];
async function loadDeals() {
if (!dealsLoaded) {
try {
var r = await fetch('api/deals.php?limit=' + DEAL_LIMIT + '&t=' + Date.now() + _dealsScopeParam());
var data = await r.json();
allDealsCache = data.deals || [];
populateDealFilters(allDealsCache);
dealsLoaded = true;
} catch(e) { console.error('loadDeals error:', e); return; }
}
applyDealFilters();
loadDealStats();
}
function reloadDeals() {
applyDealFilters();
}
// Snabbfilter via pills (Offert/Order/Pågående/Klart/Ånger)
var _dealActiveQuick = null;
var _dealQuickStatusMap = {
offert: ['offert'],
order: ['order'],
pagaende: ['projektering','bestallning','leverans','montering','besiktning'],
fardig: ['fardigstall'],
lost: ['anger','ej_godkand','avbrott']
};
function setDealQuickFilter(key){
_dealActiveQuick = (_dealActiveQuick === key) ? null : key;
// Återställ status-selecten — pillen tar över
var sel = document.getElementById('dealFilterStatus');
if(sel) sel.value = '';
_renderDealPillState();
applyDealFilters();
}
window.setDealQuickFilter = setDealQuickFilter;
function _renderDealPillState(){
var ids = { offert:'dPillOffert', order:'dPillOrder', pagaende:'dPillPagaende', fardig:'dPillFardig', lost:'dPillLost' };
Object.keys(ids).forEach(function(k){
var el = document.getElementById(ids[k]);
if(!el) return;
if(_dealActiveQuick === k){
el.style.boxShadow = '0 0 0 2px #024550';
el.style.transform = 'scale(1.03)';
} else {
el.style.boxShadow = '';
el.style.transform = '';
}
});
}
function applyDealFilters() {
var status = document.getElementById('dealFilterStatus').value;
var product = document.getElementById('dealFilterProduct').value;
var month = document.getElementById('dealFilterMonth') ? document.getElementById('dealFilterMonth').value : '';
var saljStatus = document.getElementById('dealFilterSaljStatus') ? document.getElementById('dealFilterSaljStatus').value : '';
var region = document.getElementById('dealFilterRegion') ? document.getElementById('dealFilterRegion').value : '';
var seller = document.getElementById('dealFilterSeller') ? document.getElementById('dealFilterSeller').value : '';
var quickStatuses = _dealActiveQuick ? _dealQuickStatusMap[_dealActiveQuick] : null;
allDeals = allDealsCache.filter(function(d) {
if (status && d.status !== status) return false;
if (quickStatuses && quickStatuses.indexOf(d.status) < 0) return false;
if (product && !(d.product_types || '').split(',').includes(product)) return false;
if (month && !(d.datum_salj || '').startsWith(month)) return false;
if (saljStatus && d.salj_status !== saljStatus) return false;
if (region && d.region !== region) return false;
if (seller && String(d.saljare1_id) !== seller && String(d.saljare2_id) !== seller) return false;
return true;
});
dealTotal = allDeals.length;
buildMainDealsDT(allDeals);
// Pills ska reflektera samma filter (månad + säljare) — refresh stats
if (typeof loadDealStats === 'function') loadDealStats();
}
function populateDealFilters(deals) {
var months = {};
var regions = {};
var sellers = {}; // id => name
deals.forEach(function(d) {
var ds = d.datum_salj || '';
if (ds.length >= 7) months[ds.substring(0, 7)] = true;
if (d.region) regions[d.region] = true;
if (d.saljare1_id && d.saljare1_name) sellers[d.saljare1_id] = d.saljare1_name;
if (d.saljare2_id && d.saljare2_name) sellers[d.saljare2_id] = d.saljare2_name;
});
// Säljar-filter — visas bara om >1 unik säljare
var sellerSel = document.getElementById('dealFilterSeller');
if (sellerSel) {
var sellerKeys = Object.keys(sellers);
if (sellerKeys.length > 1) {
var oldSel = sellerSel.value;
sellerSel.innerHTML = '<option value="">Alla säljare</option>'
+ sellerKeys.sort(function(a,b){ return sellers[a].localeCompare(sellers[b]); })
.map(function(id){ return '<option value="'+id+'">'+sellers[id]+'</option>'; }).join('');
sellerSel.value = oldSel;
sellerSel.style.display = '';
} else {
sellerSel.style.display = 'none';
sellerSel.value = '';
}
}
// Month filter
var keys = Object.keys(months).sort().reverse();
var sel = document.getElementById('dealFilterMonth');
if (sel) {
var old = sel.value;
sel.innerHTML = '<option value="">Alla månader</option>';
var mNames = ['','Jan','Feb','Mar','Apr','Maj','Jun','Jul','Aug','Sep','Okt','Nov','Dec'];
keys.forEach(function(m) {
var p = m.split('-');
sel.innerHTML += '<option value="' + m + '">' + p[0] + ' ' + mNames[parseInt(p[1])] + '</option>';
});
// Default = nuvarande månad (om inte användaren själv valt något annat)
var _today = new Date();
var _curMonth = _today.getFullYear() + '-' + String(_today.getMonth()+1).padStart(2,'0');
if (old) {
sel.value = old;
} else if (Array.from(sel.options).some(function(o){ return o.value === _curMonth; })) {
sel.value = _curMonth;
}
}
// Region filter
var regSel = document.getElementById('dealFilterRegion');
if (regSel) {
var oldR = regSel.value;
regSel.innerHTML = '<option value="">Alla regioner</option>';
Object.keys(regions).sort().forEach(function(r) {
regSel.innerHTML += '<option value="' + r + '">' + r + '</option>';
});
if (oldR) regSel.value = oldR;
}
}
function buildMainDealsDT(deals) {
var rows = deals.map(function(d) {
var products = (d.product_types||'').split(',').filter(Boolean).map(function(p){ return PRODUCT_LABELS[p]||p; }).join(', ');
return [
d.id,
d.deal_number || '',
d.customer_name || '',
products || '',
parseFloat(d.ordervarde_ink_moms || 0),
d.saljare1_name || '',
d.datum_salj || '',
d.status || '',
d.salj_status || '',
d.status_order || '',
d.region || ''
];
});
if (mainDealsDT) { mainDealsDT.destroy(); mainDealsDT = null; }
mainDealsDT = $('#dealsDT').DataTable({
data: rows,
columns: [
{ title:'Affärsnr', render: function(d,t,r){ return '<strong style="color:#024550;white-space:nowrap">' + r[1] + '</strong>'; } },
{ title:'Kund', render: function(d,t,r){ return r[2]; } },
{ title:'Produkter', render: function(d,t,r){ return '<span style="font-size:12px;color:#64748b">' + (r[3]||'-') + '</span>'; } },
{ title:'Ordervärde', className:'dt-right', render: function(d,t,r){
if (t === 'sort' || t === 'type') return r[4];
return r[4] ? r[4].toLocaleString('sv-SE') + ' kr' : '-';
}},
{ title:'Säljare', render: function(d,t,r){ return '<span style="font-size:12px">' + (r[5]||'-') + '</span>'; } },
{ title:'Datum', render: function(d,t,r){
if (t === 'sort' || t === 'type') return r[6];
return '<span style="font-size:12px;color:#64748b">' + (r[6]||'-') + '</span>';
}, width:'90px' },
{ title:'Status', render: function(d,t,r){
var color = DEAL_STATUS_COLORS[r[7]] || '#94a3b8';
var label = DEAL_STATUS_LABELS[r[7]] || r[7] || '-';
if (t === 'filter') return label;
return '<span style="font-size:10px;padding:2px 8px;border-radius:10px;color:#fff;background:'+color+'">'+label+'</span>';
} },
{ title:'Status sälj', render: function(d,t,r){
var color = SALJ_STATUS_COLORS[r[8]] || '#c4c4c4';
var label = SALJ_STATUS_LABELS[r[8]] || r[8] || '-';
if (t === 'filter') return label;
return '<span style="font-size:10px;padding:2px 8px;border-radius:10px;color:#fff;background:'+color+'">'+label+'</span>';
} },
{ title:'Status order', render: function(d,t,r){
if (t === 'filter') return r[9] || '-';
if (!r[9]) return '<span style="font-size:11px;color:#cbd5e1">-</span>';
return '<span style="font-size:11px;font-weight:600;color:#334155">' + r[9] + '</span>';
} },
{ title:'Region', render: function(d,t,r){
if (t === 'filter') return r[10] || '-';
if (!r[10]) return '';
return '<span style="font-size:11px;color:#64748b">' + r[10] + '</span>';
} }
],
language: {
search:'Sök:', lengthMenu:'Visa _MENU_ per sida',
info:'Visar _START_-_END_ av _TOTAL_ affärer', infoEmpty:'Inga affärer',
infoFiltered:'(filtrerat från _MAX_ totalt)',
paginate:{first:'Första',last:'Sista',next:'Nästa',previous:'Föreg.'},
zeroRecords:'Inga affärer hittades'
},
pageLength: 50,
lengthMenu: [25, 50, 100, 200],
order: [[5,'desc']],
createdRow: function(row, data) {
$(row).css('cursor','pointer').on('click', function(){ showDealDetail(data[0]); });
}
});
}
async function loadDealStats() {
try {
var monthEl = document.getElementById('dealFilterMonth');
var sellerEl = document.getElementById('dealFilterSeller');
var monthV = monthEl && monthEl.value ? '&month=' + encodeURIComponent(monthEl.value) : '';
// Säljar-filter overrider scope (admin kan filtrera ner till en specifik säljare)
var sellerV = sellerEl && sellerEl.value ? '&saljare_id=' + encodeURIComponent(sellerEl.value) : _dealsScopeParam();
const r = await fetch('api/deals.php?action=stats&t='+Date.now() + monthV + sellerV);
const stats = await r.json();
const el = (id,v) => { const e=document.getElementById(id); if(e) e.textContent=v; };
const sum = (keys) => keys.reduce((a,k) => a + (stats[k]?.count||0), 0);
const sumV = (keys) => keys.reduce((a,k) => a + (stats[k]?.value||0), 0);
el('dStatOffert', stats.offert?.count||0);
el('dSumOffert', fmtKr(stats.offert?.value||0));
el('dStatOrder', stats.order?.count||0);
el('dSumOrder', fmtKr(stats.order?.value||0));
const pKeys = ['projektering','bestallning','leverans','montering','besiktning'];
el('dStatPagaende', sum(pKeys));
el('dSumPagaende', fmtKr(sumV(pKeys)));
el('dStatFardig', stats.fardigstall?.count||0);
el('dSumFardig', fmtKr(stats.fardigstall?.value||0));
const lKeys = ['anger','ej_godkand','avbrott'];
el('dStatLost', sum(lKeys));
el('dSumLost', fmtKr(sumV(lKeys)));
} catch(e) { console.error('loadDealStats error:', e); }
}
async function showDealDetail(id) {
try {
const r = await fetch('api/deals.php?id='+id+'&t='+Date.now());
const d = await r.json();
if (d.error) { alert(d.error); return; }
const products = (d.products||[]).map(p => PRODUCT_LABELS[p.product_type]||p.product_type).join(', ');
const contacts = (d.contact_methods||[]).map(c => {
var icon = c.type==='phone'?'<svg width="14" height="14" 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>':'<svg width="14" height="14" 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>';
return '<div style="display:flex;gap:6px;align-items:center;font-size:13px;color:#334155">'+icon+' <strong>'+c.value+'</strong>'+(c.label?' <span style="font-size:11px;color:#94a3b8">('+c.label+')</span>':'')+'</div>';
}).join('');
// Pipeline progress bar
const stages = ['offert','order','projektering','bestallning','leverans','montering','besiktning','fardigstall'];
const currentIdx = stages.indexOf(d.status);
const isNeg = ['anger','ej_godkand','avbrott'].includes(d.status);
const pipelineHtml = '<div style="display:flex;gap:3px;margin:0">' + stages.map((s,i) => {
const active = !isNeg && i <= currentIdx;
const color = active ? DEAL_STATUS_COLORS[s] : '#e5e7eb';
return '<div style="flex:1;text-align:center">'
+'<div style="height:8px;background:'+color+';border-radius:4px"></div>'
+'<div style="font-size:10px;color:'+(active?'#334155':'#cbd5e1')+';margin-top:4px;font-weight:'+(active?'600':'400')+'">'+DEAL_STATUS_LABELS[s]+'</div></div>';
}).join('') + '</div>';
// Activity timeline
var timelineItems = [];
if (d.datum_salj) timelineItems.push({date:d.datum_salj, label:'Sälj', color:'#eab308', icon:'S'});
if (d.projektering_bokad) timelineItems.push({date:d.projektering_bokad, label:'Projektering bokad', color:'#3b82f6', icon:'P'});
if (d.datum_fardigstall) timelineItems.push({date:d.datum_fardigstall, label:'Färdigställt', color:'#10b981', icon:'F'});
if (d.datum_avbrott) timelineItems.push({date:d.datum_avbrott, label:'Avbrott', color:'#ef4444', icon:'A'});
// Add status log entries
(d.status_log||[]).forEach(function(l) {
timelineItems.push({date:l.created_at, label:(DEAL_STATUS_LABELS[l.new_status]||l.new_status)+(l.changed_by_name?' (av '+l.changed_by_name+')':''), color:DEAL_STATUS_COLORS[l.new_status]||'#94a3b8', icon:'\u2192', fromLog:true});
});
timelineItems.sort(function(a,b){ return b.date.localeCompare(a.date); });
var timelineHtml = timelineItems.map(function(t){
return '<div style="display:flex;gap:12px;align-items:flex-start;padding:8px 0;border-bottom:1px solid #f1f5f9">'
+'<div style="min-width:36px;height:36px;border-radius:50%;background:'+t.color+'15;color:'+t.color+';display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;flex-shrink:0">'+t.icon+'</div>'
+'<div style="flex:1"><div style="font-size:13px;font-weight:600;color:#334155">'+t.label+'</div>'
+'<div style="font-size:11px;color:#94a3b8">'+t.date+'</div></div></div>';
}).join('');
// Section helper
function secTitle(t){ return '<div style="font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin:16px 0 8px;padding-bottom:4px;border-bottom:1px solid #f1f5f9">'+t+'</div>'; }
function infoRow(label,val,opts){ if(!val) return ''; opts=opts||{}; return '<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 0;font-size:13px"><span style="color:#64748b">'+label+'</span><span style="font-weight:'+(opts.bold?'700':'500')+';color:'+(opts.color||'#334155')+'">'+val+'</span></div>'; }
// Build the full overlay
var html = '<div style="position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:10000;display:flex;align-items:flex-start;justify-content:center;padding:20px;overflow-y:auto;backdrop-filter:blur(2px)" onclick="if(event.target===this)this.remove()" id="dealDetailOverlay">'
+'<div style="background:#fff;border-radius:16px;width:100%;max-width:1100px;box-shadow:0 25px 80px rgba(0,0,0,.25);max-height:calc(100vh - 40px);display:flex;flex-direction:column">'
// Header
+'<div style="padding:20px 28px;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:flex-start;flex-shrink:0">'
+'<div style="flex:1">'
+'<div style="display:flex;align-items:center;gap:12px;margin-bottom:4px">'
+'<h2 style="font-size:22px;font-weight:800;color:#1a1a1a;margin:0">'+(d.deal_number||'Ny aff\u00e4r')+'</h2>'+(d.monday_item_id?'<a href="https://solargroup-unit.monday.com/boards/1844756189/pulses/'+d.monday_item_id+'" target="_blank" onclick="event.stopPropagation()" style="display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:8px;background:#6c3faa;color:#fff;font-size:11px;font-weight:600;text-decoration:none;white-space:nowrap" title="\u00d6ppna i Monday.com"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>Monday</a>':'')
+'<span style="padding:4px 14px;border-radius:20px;font-size:12px;font-weight:600;color:#fff;background:'+(isNeg?'#ef4444':DEAL_STATUS_COLORS[d.status]||'#94a3b8')+'">'+(DEAL_STATUS_LABELS[d.status]||d.status)+'</span>'
+(d.salj_status?'<span style="padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600;color:#fff;background:'+(SALJ_STATUS_COLORS[d.salj_status]||'#c4c4c4')+'">'+(SALJ_STATUS_LABELS[d.salj_status]||d.salj_status)+'</span>':'')
+'</div>'
+'<div style="font-size:14px;color:#64748b">'+(d.customer_name||'')+(d.customer_name2?' & '+d.customer_name2:'')
+(d.property_address?' \u2014 '+d.property_address:'')+'</div>'
+'</div>'
+'<button onclick="document.getElementById(\'dealDetailOverlay\').remove()" style="background:none;border:none;font-size:28px;cursor:pointer;color:#94a3b8;padding:0 4px;line-height:1">×</button>'
+'</div>'
// Pipeline bar
+'<div style="padding:16px 28px;border-bottom:1px solid #f1f5f9;flex-shrink:0">'+pipelineHtml+'</div>'
// Content area - scrollable
+'<div style="overflow-y:auto;padding:20px 28px 28px;flex:1">'
// Summary cards row
+'<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-bottom:20px">'
+(d.ordervarde_ink_moms?'<div style="background:linear-gradient(135deg,#024550,#036b78);border-radius:12px;padding:14px;color:#fff"><div style="font-size:10px;opacity:.7;text-transform:uppercase;letter-spacing:.5px">Orderv\u00e4rde</div><div style="font-size:20px;font-weight:800;margin-top:4px">'+fmtKr(parseFloat(d.ordervarde_ink_moms))+'</div></div>':'')
+(d.total_marginal_ink_moms?'<div style="background:#f0fdf4;border:1px solid #86efac;border-radius:12px;padding:14px"><div style="font-size:10px;color:#64748b;text-transform:uppercase;letter-spacing:.5px">Marginal</div><div style="font-size:20px;font-weight:800;color:#16a34a;margin-top:4px">'+fmtKr(parseFloat(d.total_marginal_ink_moms))+'</div></div>':'')
+(d.intakter?'<div style="background:#f0fdf4;border:1px solid #86efac;border-radius:12px;padding:14px"><div style="font-size:10px;color:#64748b;text-transform:uppercase;letter-spacing:.5px">Int\u00e4kter</div><div style="font-size:20px;font-weight:800;color:#16a34a;margin-top:4px">'+fmtKr(parseFloat(d.intakter))+'</div></div>':'')
+(d.kostnader?'<div style="background:#fef2f2;border:1px solid #fca5a5;border-radius:12px;padding:14px"><div style="font-size:10px;color:#64748b;text-transform:uppercase;letter-spacing:.5px">Kostnader</div><div style="font-size:20px;font-weight:800;color:#ef4444;margin-top:4px">'+fmtKr(parseFloat(d.kostnader))+'</div></div>':'')
+(d.ue_pris?'<div style="background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:14px"><div style="font-size:10px;color:#64748b;text-transform:uppercase;letter-spacing:.5px">UE-pris</div><div style="font-size:20px;font-weight:800;color:#9a3412;margin-top:4px">'+fmtKr(parseFloat(d.ue_pris))+'</div></div>':'')
+(d.ata?'<div style="background:#fefce8;border:1px solid #fde68a;border-radius:12px;padding:14px"><div style="font-size:10px;color:#64748b;text-transform:uppercase;letter-spacing:.5px">\u00c4TA</div><div style="font-size:20px;font-weight:800;color:#92400e;margin-top:4px">'+fmtKr(parseFloat(d.ata))+'</div></div>':'')
+'</div>'
// Three column grid
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px">'
// Column 1: Kund & Fastighet
+'<div>'
+secTitle('Kundinformation')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px">'
+'<div style="font-size:15px;font-weight:700;margin-bottom:6px">'+(d.customer_name||'')+'</div>'
+(d.personnummer?'<div style="font-size:12px;color:#64748b;margin-bottom:6px">Pnr: '+d.personnummer+'</div>':'')
+(d.customer_name2?'<div style="font-size:14px;font-weight:600;margin-top:8px">'+d.customer_name2+'</div>':'')
+(d.personnummer2?'<div style="font-size:12px;color:#64748b;margin-bottom:6px">Pnr: '+d.personnummer2+'</div>':'')
+'<div style="margin-top:8px;display:flex;flex-direction:column;gap:4px">'+contacts+'</div>'
+'</div>'
+secTitle('Fastighet')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px">'
+(d.property_address?'<div style="font-size:14px;font-weight:600;margin-bottom:4px">'+d.property_address+'</div>':'<div style="font-size:13px;color:#94a3b8">Ingen fastighet kopplad</div>')
+(d.property_city?'<div style="font-size:12px;color:#64748b;margin-bottom:6px">'+d.property_city+'</div>':'')
+infoRow('Beteckning',d.fastighetsbeteckning)
+infoRow('Huvuds\u00e4kring',d.huvudsakring?d.huvudsakring+' A':null)
+infoRow('Takmaterial',d.takmaterial)
+infoRow('Taktyp',d.taktyp)
+infoRow('N\u00e4t\u00e4gare',d.natagare)
+infoRow('Anl.id',d.anlaggningsid)
+'</div>'
+'</div>'
// Column 2: Statusar, Team, Produkt
+'<div>'
+secTitle('Statusar')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:6px">'
+infoRow('Status', DEAL_STATUS_LABELS[d.status]||d.status, {bold:true})
+infoRow('Status s\u00e4lj', d.salj_status?(SALJ_STATUS_LABELS[d.salj_status]||d.salj_status):null)
+infoRow('Status order', d.status_order, {bold:true, color:'#1e40af'})
+(d.avbrott_status?'<div style="display:flex;justify-content:space-between;align-items:center;padding:4px 0"><span style="font-size:13px;color:#64748b">Avbrott</span><span style="font-size:11px;padding:3px 12px;border-radius:10px;color:#fff;background:#ef4444;font-weight:600">'+d.avbrott_status+'</span></div>':'')
+(d.ansvarig_avbrott?infoRow('Ansvarig avbrott',d.ansvarig_avbrott):'')
+(d.datum_avbrott?infoRow('Datum avbrott',d.datum_avbrott,{color:'#ef4444'}):'')
+infoRow('AP Status', d.ap_status)
+infoRow('AP Status 2', d.ap_status_2)
+infoRow('Region', d.region, {bold:true, color:'#1e40af'})
+infoRow('F\u00f6ranm\u00e4lan', d.foranmalan)
+'</div>'
+secTitle('S\u00e4ljare & Team')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:4px">'
+infoRow('S\u00e4ljare 1', d.saljare1_name, {bold:true})
+infoRow('S\u00e4ljare 2', d.saljare2_name)
+infoRow('Bokare', d.bokare_name)
+infoRow('Projekt\u00f6r', d.projektor_name)
+infoRow('Best\u00e4llare', d.bestallare_name)
+infoRow('M\u00f6tesbokare', d.motesbokare_name)
+'</div>'
+(products?secTitle('Produkter & Teknik')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:4px">'
+infoRow('Produkter', products, {bold:true})
+infoRow('Vad \u00e4r s\u00e5lt', d.vad_ar_salt)
+infoRow('Antal solceller', d.antal_solceller)
+infoRow('M\u00e4rke solceller', d.marke_solceller)
+infoRow('Batteri storlek', d.batteri_storlek)
+infoRow('M\u00e4rke batteri', d.marke_batteri)
+infoRow('Projektnummer', d.projektnummer)
+'</div>':'')
+'</div>'
// Column 3: Ekonomi, UE, Timeline
+'<div>'
+secTitle('Ekonomi')
+'<div style="background:#f0fdf4;border:1px solid #86efac;border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:4px">'
+infoRow('Orderv\u00e4rde', d.ordervarde_ink_moms?fmtKr(parseFloat(d.ordervarde_ink_moms)):null, {bold:true, color:'#024550'})
+infoRow('Marginal', d.total_marginal_ink_moms?fmtKr(parseFloat(d.total_marginal_ink_moms)):null, {color:'#16a34a'})
+infoRow('Int\u00e4kter', d.intakter?fmtKr(parseFloat(d.intakter)):null, {bold:true, color:'#16a34a'})
+infoRow('Kostnader', d.kostnader?fmtKr(parseFloat(d.kostnader)):null, {color:'#ef4444'})
+infoRow('UE-pris', d.ue_pris?fmtKr(parseFloat(d.ue_pris)):null, {color:'#9a3412'})
+infoRow('\u00c4TA', d.ata?fmtKr(parseFloat(d.ata)):null, {color:'#92400e'})
+infoRow('GT', d.gt_belopp?fmtKr(parseFloat(d.gt_belopp)):null)
+infoRow('ROT', d.rot_belopp?fmtKr(parseFloat(d.rot_belopp)):null)
+infoRow('Finans via oss', d.finans_via_oss?fmtKr(parseFloat(d.finans_via_oss)):null)
+infoRow('Faktura', d.skapa_faktura)
+infoRow('Fakturastatus', d.faktura_status)
+infoRow('Betvillkor', d.betvillkor)
+'</div>'
+(d.tilldelad_ue?secTitle('Entrepren\u00f6r (UE)')
+'<div style="background:#fff7ed;border:1px solid #fed7aa;border-radius:10px;padding:14px">'
+'<div style="font-size:14px;font-weight:700;color:#9a3412;margin-bottom:4px">'+d.tilldelad_ue+'</div>'
+infoRow('UE-pris', d.ue_pris?fmtKr(parseFloat(d.ue_pris)):null, {color:'#9a3412',bold:true})
+'</div>':'')
+secTitle('Aktivitetslogg')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px;max-height:300px;overflow-y:auto">'
+(timelineHtml||'<div style="font-size:13px;color:#94a3b8;text-align:center;padding:16px 0">Ingen aktivitet loggad</div>')
+'</div>'
// Datum
+secTitle('Viktiga datum')
+'<div style="background:#f8f9fa;border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:4px">'
+infoRow('Datum s\u00e4lj', d.datum_salj, {bold:true})
+infoRow('Projektering bokad', d.projektering_bokad)
+infoRow('F\u00e4rdigst\u00e4llt', d.datum_fardigstall, {color:'#10b981',bold:true})
+infoRow('Avbrott', d.datum_avbrott, {color:'#ef4444'})
+'</div>'
+'</div>'
+'</div>' // end grid
// Status change buttons
+'<div style="margin-top:20px;padding-top:16px;border-top:1px solid #e5e7eb">'
+'<div style="font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px">\u00c4ndra status</div>'
+'<div style="display:flex;gap:6px;flex-wrap:wrap">'
+stages.concat(['anger','ej_godkand','avbrott']).map(function(s){ return '<button onclick="changeDealStatus('+d.id+',"'+s+'")" style="padding:5px 12px;border-radius:8px;font-size:11px;font-weight:600;border:2px solid '+(d.status===s?DEAL_STATUS_COLORS[s]:'#e5e7eb')+';background:'+(d.status===s?DEAL_STATUS_COLORS[s]:'#fff')+';color:'+(d.status===s?'#fff':'#64748b')+';cursor:pointer;transition:all .15s" onmouseover="this.style.borderColor=\''+DEAL_STATUS_COLORS[s]+'\'" onmouseout="this.style.borderColor=\''+(d.status===s?DEAL_STATUS_COLORS[s]:'#e5e7eb')+'\'">'+(DEAL_STATUS_LABELS[s]||s)+'</button>'; }).join('')
+'</div></div>'
+'</div>' // end content area
+'</div></div>'; // end overlay
document.body.insertAdjacentHTML('beforeend', html);
} catch(e) { console.error('showDealDetail error:', e); }
}
async function changeDealStatus(dealId, newStatus) {
const user = JSON.parse(localStorage.getItem('solarUser')||'{}');
try {
const r = await fetch('api/deals.php?action=status', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ deal_id: dealId, status: newStatus, staff_id: user.id||0 })
});
const data = await r.json();
if (data.success) {
const overlay = document.getElementById('dealDetailOverlay');
if (overlay) overlay.remove();
showDealDetail(dealId);
loadDeals();
}
} catch(e) { console.error('changeDealStatus error:', e); }
}