js/personal.js.bak_20260428_195833_pactive

Code: DEV-D4F2775C Size: 33.1 KB Lines: 612 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/personal.js.bak_20260428_195833_pactive

Task / Comment

Open report form
// personal.js - Staff management

// === PERSONAL ===
var currentStaffData = null;

var staffDT = null;
var allStaffData = [];

async function loadStaff() {
    var role = '';
    var rf = document.getElementById('staffRoleFilter');
    if (rf) role = rf.value || '';
    var url = '/api/staff.php?active=0';
    if (role) url += '&role=' + role;
    try {
        var res = await fetch(url);
        var staff = await res.json();
        if (!Array.isArray(staff)) staff = [];
        allStaffData = staff;
        var cnt = document.getElementById('staffCount');
        if (cnt) cnt.textContent = staff.length + ' person' + (staff.length !== 1 ? 'er' : '');
        buildStaffDT(staff);
    } catch (e) {
        console.error('loadStaff error:', e);
    }
}

function buildStaffDT(staff) {
    var rows = staff.map(function(s) {
        var roleLbl = roleLabels[s.role] || s.role || '';
        var roleClr = roleColors[s.role] || 'gray';
        var sales = parseFloat(s.total_sales || 0);
        return [
            s.id,
            s.name || '',
            s.email || '',
            roleLbl,
            s.active == 1 ? 'Aktiv' : 'Inaktiv',
            sales,
            roleClr
        ];
    });

    if (staffDT) { staffDT.destroy(); staffDT = null; }
    staffDT = $('#staffDT').DataTable({
        data: rows,
        columns: [
            { title:'Namn', render: function(d,t,r){ return '<strong>' + r[1] + '</strong>'; } },
            { title:'E-post', render: function(d,t,r){ return r[2]; } },
            { title:'Roll', render: function(d,t,r){ return '<span class="status-badge ' + r[6] + '">' + r[3] + '</span>'; } },
            { title:'Status', render: function(d,t,r){ return r[4] === 'Aktiv' ? '<span class="status-badge green">Aktiv</span>' : '<span class="status-badge gray">Inaktiv</span>'; }, width:'80px' },
            { title:'Försäljning', className:'dt-right', render: function(d,t,r){ return r[5] ? r[5].toLocaleString('sv-SE') + ' kr' : '-'; } }
        ],
        language: {
            search:'Sök:', lengthMenu:'Visa _MENU_ per sida',
            info:'Visar _START_-_END_ av _TOTAL_ personal', infoEmpty:'Ingen personal',
            infoFiltered:'(filtrerat från _MAX_ totalt)',
            paginate:{first:'Första',last:'Sista',next:'Nästa',previous:'Föreg.'},
            zeroRecords:'Ingen personal hittades'
        },
        pageLength: 50,
        lengthMenu: [25, 50, 100],
        order: [[0,'asc']],
        createdRow: function(row, data) {
            $(row).css('cursor','pointer').on('click', function(){ showStaffDetail(data[0]); });
        }
    });
}

// --- Staff Detail View ---
async function showStaffDetail(id) {
    try {
        const res = await fetch(STAFF_API + '?id=' + id);
        const staff = await res.json();
        if (staff.error) return;
        currentStaffData = staff;
        document.getElementById('staffListView').style.display = 'none';
        document.getElementById('staffDetailView').style.display = 'block';
        document.getElementById('staffDetailName').textContent = staff.name;
        document.getElementById('staffDetailRole').textContent = roleLabels[staff.role] || staff.role;
        document.getElementById('staffDetailRole').className = 'status-badge ' + (roleColors[staff.role] || 'gray');
        switchStaffTab('info');
    } catch(e) { console.error('showStaffDetail error:', e); }
}

function closeStaffDetail() {
    document.getElementById('staffDetailView').style.display = 'none';
    document.getElementById('staffListView').style.display = 'block';
    currentStaffData = null;
}

function switchStaffTab(tab) {
    document.querySelectorAll('.staff-tab').forEach(function(btn) {
        var isActive = btn.dataset.tab === tab;
        btn.style.borderBottomColor = isActive ? '#024550' : 'transparent';
        btn.style.color = isActive ? '#024550' : '#64748b';
    });
    var panels = {info:'staffTabInfo',loner:'staffTabLoner',salj:'staffTabSalj',utveckling:'staffTabUtveckling',affarslista:'staffTabAffarslista'};
    Object.keys(panels).forEach(function(k){ document.getElementById(panels[k]).style.display = k === tab ? 'block' : 'none'; });
    if (!currentStaffData) return;
    var sid = currentStaffData.id;
    if (tab === 'info') renderStaffInfo(currentStaffData);
    else if (tab === 'loner') loadStaffLoner(sid);
    else if (tab === 'salj') loadStaffMeetings(sid, 'salj_samtal');
    else if (tab === 'utveckling') loadStaffMeetings(sid, 'utvecklings_samtal');
    else if (tab === 'affarslista') loadStaffDeals(sid);
}

function staffInfoField(label, value) {
    return '<div><div style="font-size:11px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;margin-bottom:2px">' + label + '</div><div style="font-size:14px;color:#1a1a1a;font-weight:500">' + (value || '-') + '</div></div>';
}

function renderStaffInfo(s) {
    document.getElementById('staffTabInfo').innerHTML =
        '<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:800px">' +
        '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:20px">' +
        '<h3 style="font-size:14px;font-weight:700;color:#024550;margin-bottom:16px">Personuppgifter</h3>' +
        '<div style="display:grid;gap:12px">' +
        staffInfoField('Namn', s.name) +
        staffInfoField('E-post', s.email) +
        staffInfoField('Telefon', s.phone) +
        staffInfoField('Titel', s.title) +
        staffInfoField('Personnummer', s.personnummer) +
        '</div></div>' +
        '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:20px">' +
        '<h3 style="font-size:14px;font-weight:700;color:#024550;margin-bottom:16px">Anställning</h3>' +
        '<div style="display:grid;gap:12px">' +
        staffInfoField('Roll', roleLabels[s.role] || s.role) +
        staffInfoField('Startdatum', s.start_date) +
        staffInfoField('Grundlön', s.grundlon ? parseFloat(s.grundlon).toLocaleString('sv-SE') + ' kr' : null) +
        staffInfoField('Skattesats', (s.skatt_procent || '30') + '%') +
        staffInfoField('Status', s.active == 1 ? 'Aktiv' : 'Inaktiv') +
        '</div></div>' +
        '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:20px">' +
        '<h3 style="font-size:14px;font-weight:700;color:#024550;margin-bottom:16px">Adress</h3>' +
        '<div style="display:grid;gap:12px">' +
        staffInfoField('Adress', s.address) +
        staffInfoField('Postnummer', s.zip) +
        staffInfoField('Stad', s.city) +
        '</div></div>' +
        '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:20px">' +
        '<h3 style="font-size:14px;font-weight:700;color:#024550;margin-bottom:16px">System</h3>' +
        '<div style="display:grid;gap:12px">' +
        staffInfoField('Google', s.google_id ? '✓ Kopplad' : 'Ej kopplad') +
        staffInfoField('Senast inloggning', s.last_login ? new Date(s.last_login).toLocaleDateString('sv-SE') : null) +
        staffInfoField('Skapad', s.created_at ? new Date(s.created_at).toLocaleDateString('sv-SE') : null) +
        '</div>' +
        '<button onclick="editStaff(' + s.id + ')" style="margin-top:16px;padding:8px 16px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:13px;cursor:pointer;font-family:inherit">✎ Redigera</button>' +
        '</div></div>';
}

// --- Löner ---
async function loadStaffLoner(staffId) {
    var panel = document.getElementById('staffTabLoner');
    panel.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8">Laddar löner...</div>';
    try {
        var res = await fetch('api/salary.php?action=my&staff_id=' + staffId);
        var records = await res.json();
        if (!records || !records.length) {
            panel.innerHTML = '<div style="padding:40px;text-align:center;color:#94a3b8;background:#fff;border-radius:12px;border:1px solid #e5e7eb">Inga lönebesked registrerade</div>';
            return;
        }
        var html = '<div style="max-width:900px">';
        records.forEach(function(r) {
            var brutto = parseFloat(r.brutto || 0);
            var netto = parseFloat(r.netto || 0);
            var skatt = parseFloat(r.skatt || 0);
            html += '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:16px 20px;margin-bottom:12px">' +
                '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                '<div style="font-size:16px;font-weight:700;color:#024550">' + r.period + '</div>' +
                '<div style="display:flex;gap:20px;font-size:13px">' +
                '<span>Brutto: <strong>' + brutto.toLocaleString('sv-SE') + ' kr</strong></span>' +
                '<span>Skatt: <strong style="color:#ef4444">' + skatt.toLocaleString('sv-SE') + ' kr</strong></span>' +
                '<span>Netto: <strong style="color:#10b981">' + netto.toLocaleString('sv-SE') + ' kr</strong></span>' +
                '</div></div>' +
                '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px;font-size:12px;color:#64748b">' +
                '<div>Grundlön: ' + parseFloat(r.grundlon||0).toLocaleString('sv-SE') + ' kr</div>' +
                '<div>Provision: ' + parseFloat(r.provision||0).toLocaleString('sv-SE') + ' kr</div>' +
                '<div>Milersättning: ' + parseFloat(r.milersattning||0).toLocaleString('sv-SE') + ' kr</div>' +
                '<div>Traktamente: ' + parseFloat(r.traktamente||0).toLocaleString('sv-SE') + ' kr</div>' +
                (r.ovriga_tillagg && parseFloat(r.ovriga_tillagg) ? '<div>Övriga: ' + parseFloat(r.ovriga_tillagg).toLocaleString('sv-SE') + ' kr</div>' : '') +
                '</div>';
            if (r.provisions && r.provisions.length) {
                html += '<div style="margin-top:10px;padding-top:10px;border-top:1px solid #f1f5f9">' +
                    '<div style="font-size:11px;color:#94a3b8;margin-bottom:4px">Provisionsdetaljer:</div>';
                r.provisions.forEach(function(p) {
                    html += '<div style="font-size:12px;display:flex;justify-content:space-between;padding:2px 0"><span>' + (p.description || p.deal_title || '#' + p.deal_id) + '</span><span style="font-weight:600">' + parseFloat(p.amount).toLocaleString('sv-SE') + ' kr</span></div>';
                });
                html += '</div>';
            }
            html += '</div>';
        });
        html += '</div>';
        panel.innerHTML = html;
    } catch(e) { panel.innerHTML = '<div style="padding:20px;color:#ef4444">Fel: ' + e.message + '</div>'; }
}

// --- Möten (Sälj samtal & Utvecklings samtal) ---
async function loadStaffMeetings(staffId, type) {
    var panelId = type === 'salj_samtal' ? 'staffTabSalj' : 'staffTabUtveckling';
    var panel = document.getElementById(panelId);
    var label = type === 'salj_samtal' ? 'Sälj samtal' : 'Utvecklings samtal';
    panel.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8">Laddar...</div>';
    try {
        var res = await fetch('api/staff-meetings.php?staff_id=' + staffId + '&type=' + type);
        var meetings = await res.json();
        var html = '<div style="max-width:800px">' +
            '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">' +
            '<h3 style="font-size:16px;font-weight:700;color:#024550;margin:0">' + label + '</h3>' +
            '<button onclick="showMeetingForm(' + staffId + ',\'' + type + '\')" style="padding:8px 16px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:13px;cursor:pointer;font-family:inherit">+ Nytt samtal</button></div>';
        if (!meetings || !meetings.length) {
            html += '<div style="padding:40px;text-align:center;color:#94a3b8;background:#fff;border-radius:12px;border:1px solid #e5e7eb">Inga ' + label.toLowerCase() + ' registrerade</div>';
        } else {
            meetings.forEach(function(m) {
                var actions = '';
                if (m.action_points) {
                    try {
                        var pts = JSON.parse(m.action_points);
                        if (Array.isArray(pts)) actions = pts.map(function(a){ return '<li style="font-size:12px;margin-bottom:4px">' + a + '</li>'; }).join('');
                    } catch(e) { actions = '<li style="font-size:12px">' + m.action_points + '</li>'; }
                }
                html += '<div style="background:#fff;border-radius:12px;border:1px solid #e5e7eb;padding:16px 20px;margin-bottom:12px">' +
                    '<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">' +
                    '<div><div style="font-size:14px;font-weight:600;color:#1a1a1a">' + (m.title || label) + '</div>' +
                    '<div style="font-size:12px;color:#94a3b8;margin-top:2px">' + (m.meeting_date || '') + (m.conducted_by_name ? ' — ' + m.conducted_by_name : '') + '</div></div>' +
                    '<div style="display:flex;gap:4px">' +
                    '<button onclick="editMeetingInline(' + m.id + ',' + staffId + ',\'' + type + '\')" style="background:none;border:none;cursor:pointer;color:#3b82f6;font-size:14px" title="Redigera">✎</button>' +
                    '<button onclick="deleteMeeting(' + m.id + ',' + staffId + ',\'' + type + '\')" style="background:none;border:none;cursor:pointer;color:#ef4444;font-size:14px" title="Ta bort">✕</button>' +
                    '</div></div>' +
                    (m.notes ? '<div style="font-size:13px;color:#374151;margin-bottom:8px;white-space:pre-wrap">' + m.notes + '</div>' : '') +
                    (actions ? '<div style="margin-top:8px"><div style="font-size:11px;color:#94a3b8;margin-bottom:4px">Åtgärdspunkter:</div><ul style="margin:0;padding-left:20px">' + actions + '</ul></div>' : '') +
                    (m.next_meeting ? '<div style="margin-top:8px;font-size:12px;color:#3b82f6">Nästa möte: ' + m.next_meeting + '</div>' : '') +
                    '</div>';
            });
        }
        html += '</div>';
        panel.innerHTML = html;
    } catch(e) { panel.innerHTML = '<div style="padding:20px;color:#ef4444">Fel: ' + e.message + '</div>'; }
}

function showMeetingForm(staffId, type, existing) {
    var label = type === 'salj_samtal' ? 'Sälj samtal' : 'Utvecklings samtal';
    var panelId = type === 'salj_samtal' ? 'staffTabSalj' : 'staffTabUtveckling';
    var panel = document.getElementById(panelId);
    var today = new Date().toISOString().split('T')[0];
    var title = existing ? (existing.title || '') : '';
    var date = existing ? (existing.meeting_date || today) : today;
    var notes = existing ? (existing.notes || '') : '';
    var actPts = '';
    if (existing && existing.action_points) {
        try { var arr = JSON.parse(existing.action_points); actPts = Array.isArray(arr) ? arr.join('\n') : existing.action_points; } catch(e) { actPts = existing.action_points; }
    }
    var nextM = existing ? (existing.next_meeting || '') : '';
    var mid = existing ? existing.id : 0;

    var formHtml = '<div id="meetingFormBox" style="background:#f8fafc;border:2px solid #024550;border-radius:12px;padding:20px;margin-bottom:20px">' +
        '<h4 style="font-size:14px;font-weight:700;color:#024550;margin-bottom:16px">' + (mid ? 'Redigera' : 'Nytt') + ' ' + label + '</h4>' +
        '<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px">' +
        '<div><label style="font-size:12px;color:#64748b;display:block;margin-bottom:4px">Titel</label><input type="text" id="mtgTitle" value="' + title.replace(/"/g,'&quot;') + '" style="width:100%;padding:8px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;font-family:inherit;box-sizing:border-box"></div>' +
        '<div><label style="font-size:12px;color:#64748b;display:block;margin-bottom:4px">Datum</label><input type="date" id="mtgDate" value="' + date + '" style="width:100%;padding:8px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;font-family:inherit;box-sizing:border-box"></div></div>' +
        '<div style="margin-bottom:12px"><label style="font-size:12px;color:#64748b;display:block;margin-bottom:4px">Anteckningar</label><textarea id="mtgNotes" rows="4" style="width:100%;padding:8px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;font-family:inherit;resize:vertical;box-sizing:border-box">' + notes.replace(/</g,'&lt;') + '</textarea></div>' +
        '<div style="margin-bottom:12px"><label style="font-size:12px;color:#64748b;display:block;margin-bottom:4px">Åtgärdspunkter (en per rad)</label><textarea id="mtgActions" rows="3" style="width:100%;padding:8px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;font-family:inherit;resize:vertical;box-sizing:border-box">' + actPts.replace(/</g,'&lt;') + '</textarea></div>' +
        '<div style="margin-bottom:16px"><label style="font-size:12px;color:#64748b;display:block;margin-bottom:4px">Nästa möte</label><input type="date" id="mtgNext" value="' + nextM + '" style="padding:8px 12px;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;font-family:inherit"></div>' +
        '<div style="display:flex;gap:10px">' +
        '<button onclick="saveMeeting(' + staffId + ',\'' + type + '\',' + mid + ')" style="padding:8px 20px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:13px;cursor:pointer;font-family:inherit">Spara</button>' +
        '<button onclick="loadStaffMeetings(' + staffId + ',\'' + type + '\')" style="padding:8px 20px;background:#e5e7eb;color:#374151;border:none;border-radius:8px;font-size:13px;cursor:pointer;font-family:inherit">Avbryt</button>' +
        '</div></div>';

    var old = document.getElementById('meetingFormBox');
    if (old) old.remove();
    var header = panel.querySelector('h3');
    if (header && header.parentElement) header.parentElement.insertAdjacentHTML('afterend', formHtml);
    else panel.insertAdjacentHTML('afterbegin', formHtml);
}

async function saveMeeting(staffId, type, meetingId) {
    var actLines = document.getElementById('mtgActions').value.trim().split('\n').filter(function(l){ return l.trim(); });
    var body = {
        title: document.getElementById('mtgTitle').value.trim(),
        meeting_date: document.getElementById('mtgDate').value,
        notes: document.getElementById('mtgNotes').value.trim(),
        action_points: actLines.length ? JSON.stringify(actLines) : null,
        next_meeting: document.getElementById('mtgNext').value || null
    };
    try {
        if (meetingId) {
            body.id = meetingId;
            await fetch('api/staff-meetings.php?action=update', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body) });
        } else {
            body.staff_id = staffId;
            body.type = type;
            body.conducted_by = gStaffId || null;
            await fetch('api/staff-meetings.php', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body) });
        }
        loadStaffMeetings(staffId, type);
    } catch(e) { alert('Fel: ' + e.message); }
}

async function editMeetingInline(meetingId, staffId, type) {
    try {
        var res = await fetch('api/staff-meetings.php?id=' + meetingId);
        var m = await res.json();
        if (m) showMeetingForm(staffId, type, m);
    } catch(e) { alert('Fel: ' + e.message); }
}

async function deleteMeeting(meetingId, staffId, type) {
    if (!confirm('Ta bort detta samtal?')) return;
    try {
        await fetch('api/staff-meetings.php?id=' + meetingId, { method:'DELETE' });
        loadStaffMeetings(staffId, type);
    } catch(e) { alert('Fel: ' + e.message); }
}

// --- Staff Deals ---
var staffDealsDT = null;
var staffDealsRaw = [];
var staffDealsFiltered = [];

async function loadStaffDeals(staffId) {
    var panel = document.getElementById('staffTabAffarslista');
    panel.innerHTML = '<div style="padding:20px;text-align:center;color:#94a3b8">Laddar affärer...</div>';
    try {
        var res = await fetch('api/deals.php?saljare_id=' + staffId + '&limit=200');
        var data = await res.json();
        staffDealsRaw = Array.isArray(data) ? data : (data.deals || []);
        if (!staffDealsRaw.length) {
            panel.innerHTML = '<div style="padding:40px;text-align:center;color:#94a3b8;background:#fff;border-radius:12px;border:1px solid #e5e7eb">Inga affärer</div>';
            return;
        }
        renderStaffDealsPanel(staffId);
    } catch(e) { panel.innerHTML = '<div style="padding:20px;color:#ef4444">Fel: ' + e.message + '</div>'; }
}

function renderStaffDealsPanel(staffId) {
    var panel = document.getElementById('staffTabAffarslista');

    // Collect unique months and statuses
    var months = {};
    var statuses = {};
    staffDealsRaw.forEach(function(d) {
        var ds = d.datum_salj || '';
        if (ds.length >= 7) months[ds.substring(0, 7)] = true;
        if (d.status) statuses[d.status] = true;
    });
    var monthKeys = Object.keys(months).sort().reverse();
    var statusKeys = Object.keys(statuses).sort();

    // Build filter bar
    var html = '<div style="margin-bottom:12px;display:flex;flex-wrap:wrap;gap:8px;align-items:center">' +
        '<select id="sdMonth" onchange="applyStaffDealsFilter(' + staffId + ')" style="padding:6px 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:12px;font-family:inherit">' +
        '<option value="">Alla månader</option>';
    monthKeys.forEach(function(m) {
        var parts = m.split('-');
        var label = parts[0] + ' ' + ['','Jan','Feb','Mar','Apr','Maj','Jun','Jul','Aug','Sep','Okt','Nov','Dec'][parseInt(parts[1])];
        html += '<option value="' + m + '">' + label + '</option>';
    });
    html += '</select>' +
        '<select id="sdStatus" onchange="applyStaffDealsFilter(' + staffId + ')" style="padding:6px 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:12px;font-family:inherit">' +
        '<option value="">Alla statusar</option>';
    statusKeys.forEach(function(s) {
        html += '<option value="' + s + '">' + (DEAL_STATUS_LABELS[s] || s) + '</option>';
    });
    html += '</select>' +
        '<button onclick="document.getElementById(\'sdMonth\').value=\'\';document.getElementById(\'sdStatus\').value=\'\';applyStaffDealsFilter(' + staffId + ')" style="padding:6px 12px;background:#f1f5f9;border:1px solid #e5e7eb;border-radius:8px;font-size:12px;cursor:pointer;font-family:inherit">Rensa</button>' +
        '<div id="sdSumBox" style="margin-left:auto;font-size:13px;font-weight:600;color:#024550"></div>' +
        '</div>';

    // Stats cards
    html += '<div id="sdStats" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:16px"></div>';

    // Table
    html += '<table id="staffDealsDT" class="display" style="width:100%"><thead><tr>' +
        '<th>Affärsnr</th><th>Kund</th><th>Status</th><th>Säljstatus</th><th style="text-align:right">Värde</th><th>Datum</th>' +
        '</tr></thead><tbody></tbody></table>';

    panel.innerHTML = html;
    applyStaffDealsFilter(staffId);
}

function applyStaffDealsFilter(staffId) {
    var selMonth = document.getElementById('sdMonth') ? document.getElementById('sdMonth').value : '';
    var selStatus = document.getElementById('sdStatus') ? document.getElementById('sdStatus').value : '';

    staffDealsFiltered = staffDealsRaw.filter(function(d) {
        if (selMonth) {
            var ds = d.datum_salj || '';
            if (ds.substring(0, 7) !== selMonth) return false;
        }
        if (selStatus && d.status !== selStatus) return false;
        return true;
    });

    // Compute sum
    var totalSum = 0;
    staffDealsFiltered.forEach(function(d) { totalSum += parseFloat(d.ordervarde_ink_moms || 0); });
    var sumBox = document.getElementById('sdSumBox');
    if (sumBox) sumBox.textContent = 'Summa: ' + totalSum.toLocaleString('sv-SE') + ' kr (' + staffDealsFiltered.length + ' affärer)';

    // Compute stats from ALL data (unfiltered)
    renderStaffDealStats();

    // Build DataTable
    buildStaffDealsDT(staffDealsFiltered);
}

function renderStaffDealStats() {
    var el = document.getElementById('sdStats');
    if (!el) return;
    var all = staffDealsRaw;
    if (!all.length) { el.innerHTML = ''; return; }

    // Group by month
    var byMonth = {};
    var totalVal = 0;
    all.forEach(function(d) {
        var ds = d.datum_salj || '';
        var m = ds.length >= 7 ? ds.substring(0, 7) : 'unknown';
        if (!byMonth[m]) byMonth[m] = { count: 0, value: 0, orders: 0 };
        byMonth[m].count++;
        var v = parseFloat(d.ordervarde_ink_moms || 0);
        byMonth[m].value += v;
        totalVal += v;
        if (d.status === 'order') byMonth[m].orders++;
    });

    var monthKeys = Object.keys(byMonth).filter(function(k){ return k !== 'unknown'; }).sort();
    var numMonths = monthKeys.length || 1;

    var avgPerMonth = all.length / numMonths;
    var totalOrders = all.filter(function(d){ return d.status === 'order'; }).length;
    var avgOrdersPerMonth = totalOrders / numMonths;
    var avgValuePerMonth = totalVal / numMonths;

    // Current month trend
    var now = new Date();
    var curKey = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0');
    var curMonth = byMonth[curKey] || { count: 0, value: 0, orders: 0 };
    var dayOfMonth = now.getDate();
    var daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
    var projectedCount = dayOfMonth > 0 ? Math.round(curMonth.count / dayOfMonth * daysInMonth) : 0;
    var projectedValue = dayOfMonth > 0 ? Math.round(curMonth.value / dayOfMonth * daysInMonth) : 0;
    var trending = projectedCount > avgPerMonth;
    var trendPct = avgPerMonth > 0 ? Math.round((projectedCount - avgPerMonth) / avgPerMonth * 100) : 0;

    function statCard(title, value, sub, color) {
        return '<div style="background:#fff;border-radius:10px;border:1px solid #e5e7eb;padding:12px 14px">' +
            '<div style="font-size:11px;color:#94a3b8;text-transform:uppercase;letter-spacing:.3px">' + title + '</div>' +
            '<div style="font-size:20px;font-weight:700;color:' + (color || '#024550') + ';margin:4px 0">' + value + '</div>' +
            (sub ? '<div style="font-size:11px;color:#64748b">' + sub + '</div>' : '') +
            '</div>';
    }

    el.innerHTML =
        statCard('Totalt', all.length + ' affärer', totalVal.toLocaleString('sv-SE') + ' kr') +
        statCard('Snitt/mån', avgPerMonth.toFixed(1) + ' affärer', avgValuePerMonth.toLocaleString('sv-SE', {maximumFractionDigits:0}) + ' kr/mån') +
        statCard('Order snitt/mån', avgOrdersPerMonth.toFixed(1), totalOrders + ' totalt') +
        statCard('Denna månad', curMonth.count + ' affärer',
            curMonth.value.toLocaleString('sv-SE') + ' kr') +
        statCard('Prognos månad', projectedCount + ' affärer',
            projectedValue.toLocaleString('sv-SE') + ' kr') +
        statCard('Trend',
            (trending ? '&#9650; +' : '&#9660; ') + trendPct + '%',
            trending ? 'Bättre än snitt' : 'Under snitt',
            trending ? '#10b981' : '#ef4444');
}

function buildStaffDealsDT(deals) {
    var rows = deals.map(function(d) {
        return [
            d.id,
            d.deal_number || '',
            d.customer_name || '',
            d.status || '',
            d.salj_status || '',
            parseFloat(d.ordervarde_ink_moms || 0),
            d.datum_salj || ''
        ];
    });

    if (staffDealsDT) { staffDealsDT.destroy(); staffDealsDT = null; }
    staffDealsDT = $('#staffDealsDT').DataTable({
        data: rows,
        columns: [
            { title:'Affärsnr', render: function(d,t,r){ return '<strong style="color:#024550">' + r[1] + '</strong>'; } },
            { title:'Kund', render: function(d,t,r){ return r[2]; } },
            { title:'Status', render: function(d,t,r){
                var color = DEAL_STATUS_COLORS[r[3]] || '#94a3b8';
                var label = DEAL_STATUS_LABELS[r[3]] || r[3] || '-';
                return '<span style="font-size:10px;padding:2px 8px;border-radius:10px;color:#fff;background:'+color+'">'+label+'</span>';
            }},
            { title:'Säljstatus', render: function(d,t,r){ return r[4] || '-'; }, width:'90px' },
            { title:'Värde', className:'dt-right', render: function(d,t,r){
                return r[5] ? r[5].toLocaleString('sv-SE') + ' kr' : '-';
            }},
            { title:'Datum', render: function(d,t,r){ return '<span style="font-size:12px;color:#64748b">'+r[6]+'</span>'; }, width:'100px' }
        ],
        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: 25,
        order: [[5,'desc']],
        createdRow: function(row, data) {
            $(row).css('cursor','pointer').on('click', function(){ showDealDetail(data[0]); });
        }
    });
}

function showStaffModal(staffData) {
    var modal = document.getElementById('staffModal');
    document.getElementById('staffModalTitle').textContent = staffData ? 'Redigera personal' : 'Lägg till personal';
    document.getElementById('staffEditId').value = staffData ? staffData.id : '';
    document.getElementById('staffName').value = staffData ? staffData.name : '';
    document.getElementById('staffEmail').value = staffData ? (staffData.email || '') : '';
    document.getElementById('staffRole').value = staffData ? staffData.role : 'saljare';
    document.getElementById('staffPhone').value = staffData ? (staffData.phone || '') : '';
    document.getElementById('staffTitle').value = staffData ? (staffData.title || '') : '';
    document.getElementById('staffPnr').value = staffData ? (staffData.personnummer || '') : '';
    document.getElementById('staffAddress').value = staffData ? (staffData.address || '') : '';
    document.getElementById('staffZip').value = staffData ? (staffData.zip || '') : '';
    document.getElementById('staffCity').value = staffData ? (staffData.city || '') : '';
    document.getElementById('staffStartDate').value = staffData ? (staffData.start_date || '') : '';
    document.getElementById('staffGrundlon').value = staffData ? (staffData.grundlon || '') : '';
    document.getElementById('staffSkatt').value = staffData ? (staffData.skatt_procent || '30') : '30';
    document.getElementById('staffModalError').style.display = 'none';
    modal.style.display = 'flex';
}

function closeStaffModal() {
    document.getElementById('staffModal').style.display = 'none';
}

async function saveStaff() {
    var id = document.getElementById('staffEditId').value;
    var data = {
        name: document.getElementById('staffName').value.trim(),
        email: document.getElementById('staffEmail').value.trim(),
        role: document.getElementById('staffRole').value,
        phone: document.getElementById('staffPhone').value.trim() || null,
        title: document.getElementById('staffTitle').value.trim() || null,
        personnummer: document.getElementById('staffPnr').value.trim() || null,
        address: document.getElementById('staffAddress').value.trim() || null,
        zip: document.getElementById('staffZip').value.trim() || null,
        city: document.getElementById('staffCity').value.trim() || null,
        start_date: document.getElementById('staffStartDate').value || null,
        grundlon: document.getElementById('staffGrundlon').value ? parseFloat(document.getElementById('staffGrundlon').value) : null,
        skatt_procent: document.getElementById('staffSkatt').value ? parseFloat(document.getElementById('staffSkatt').value) : 30,
    };
    if (!data.name || !data.email) {
        const err = document.getElementById('staffModalError');
        err.textContent = 'Namn och e-post krävs';
        err.style.display = 'block';
        return;
    }
    try {
        const url = id ? STAFF_API + '?id=' + id : STAFF_API;
        const res = await fetch(url, {
            method: id ? 'PUT' : 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(data),
        });
        const result = await res.json();
        if (!res.ok) {
            const err = document.getElementById('staffModalError');
            err.textContent = result.error || 'Något gick fel';
            err.style.display = 'block';
            return;
        }
        closeStaffModal();
        loadStaff();
        // Refresh detail view if open
        if (currentStaffData && currentStaffData.id == id) {
            showStaffDetail(parseInt(id));
        }
    } catch (e) {
        var err = document.getElementById('staffModalError');
        err.textContent = e.message;
        err.style.display = 'block';
    }
}

async function editStaff(id) {
    try {
        var res = await fetch(STAFF_API + '?id=' + id);
        var staff = await res.json();
        showStaffModal(staff);
    } catch (e) {
        alert('Kunde inte ladda: ' + e.message);
    }
}

async function toggleStaffActive(id, currentActive) {
    try {
        await fetch(STAFF_API + '?id=' + id, {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({active: currentActive == 1 ? 0 : 1}),
        });
        loadStaff();
    } catch (e) {
        alert('Fel: ' + e.message);
    }
}


    
// Monday.com settings