js/fieldsales.js

Code: DEV-BFF54017 Size: 13.7 KB Lines: 232 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/fieldsales.js

Task / Comment

Open report form
// fieldsales.js - NEW (engelska filnamn)
// Innehåller: route management (saveRoute, loadRoute, deleteRoute, modal-UI)
// + bridge för legacy faltsalj.js (under migration)

(function () {
    if (window.__fieldSalesLegacyLoaded) return;
    window.__fieldSalesLegacyLoaded = true;

    var legacyPath = 'js/faltsalj.js?v=' + Date.now();
    var script = document.createElement('script');
    script.src = legacyPath;
    script.defer = false;
    document.head.appendChild(script);
})();

/* ============================================================
 * ROUTE MANAGEMENT — flyttat från faltsalj.js 2026-04-28
 * Globala funktioner: updateRouteInfoBar, showSaveRouteModal,
 * saveRoute, showLoadRouteModal, loadRoute, deleteRoute
 * Beroenden (från faltsalj.js): faltProspects, currentRouteId,
 * gStaffId, _faltGetStaffList, _faltDayMs (m.fl.)
 * ============================================================ */
function updateRouteInfoBar(){
    const bar = document.getElementById('currentRouteInfo');
    if(!bar) return;
    if(currentRouteId){
        bar.style.display = 'block';
        bar.innerHTML = '<strong>Aktiv rutt #'+currentRouteId+'</strong> — Ändringar sparas automatiskt';
    } else {
        bar.style.display = 'none';
    }
}

async function showSaveRouteModal(){
    if(faltProspects.length === 0){
        alert('Lägg till prospekt i rutten först');
        return;
    }
    await loadStaffList();
    const currentStaff = staffList.find(s => s.email === (localStorage.getItem('userEmail')||''));
    const currentId = currentStaff ? currentStaff.id : 43; // default admin

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

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:500px;width:100%;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        +'<div style="padding:20px 24px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center">'
        +'<h2 style="font-size:18px;font-weight:700;color:#1a1a1a;margin:0">'+(currentRouteId?'Uppdatera rutt':'Spara rutt')+'</h2>'
        +'<button onclick="this.closest(\'#routeSaveModal\').remove()" style="background:none;border:none;cursor:pointer;padding:4px"><svg viewBox="0 0 24 24" style="width:20px;height:20px;stroke:#94a3b8;fill:none;stroke-width:2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>'
        +'</div>'
        +'<div style="padding:24px">'
        +'<div style="margin-bottom:16px">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b;display:block;margin-bottom:6px">RUTTNAMN</label>'
        +'<input id="routeName" value="Rutt '+new Date().toLocaleDateString('sv-SE')+'" style="width:100%;padding:10px 14px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;box-sizing:border-box">'
        +'</div>'
        +'<div style="margin-bottom:16px">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b;display:block;margin-bottom:6px">DATUM</label>'
        +'<input id="routeDate" type="date" value="'+new Date().toISOString().split('T')[0]+'" style="width:100%;padding:10px 14px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;box-sizing:border-box">'
        +'</div>'
        +'<div style="margin-bottom:16px">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b;display:block;margin-bottom:6px">TILLDELA TILL</label>'
        +'<select id="routeAssignee" style="width:100%;padding:10px 14px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;box-sizing:border-box">'
        +'<option value="">Mig själv</option>'
        +staffList.filter(s=>s.active!==0).map(s => '<option value="'+s.id+'"'+(s.id==currentId?' selected':'')+'>'+s.name+'</option>').join('')
        +'</select>'
        +'</div>'
        +'<div style="margin-bottom:20px">'
        +'<label style="font-size:12px;font-weight:600;color:#64748b;display:block;margin-bottom:6px">ANTECKNINGAR</label>'
        +'<textarea id="routeNotes" rows="2" placeholder="T.ex. fokusområde, mål..." style="width:100%;padding:10px 14px;border:1.5px solid #e5e7eb;border-radius:8px;font-size:14px;font-family:inherit;resize:vertical;box-sizing:border-box"></textarea>'
        +'</div>'
        +'<div style="background:#f8fafc;border-radius:10px;padding:14px;margin-bottom:20px">'
        +'<div style="font-size:12px;color:#64748b;margin-bottom:4px">Rutten innehåller</div>'
        +'<div style="font-size:20px;font-weight:700;color:#1a1a1a">'+faltProspects.length+' prospekt</div>'
        +'<div style="font-size:12px;color:#64748b">'+faltProspects.filter(p=>p.status==='interested').length+' leads, '+faltProspects.filter(p=>p.status==='unvisited').length+' obesökta</div>'
        +'</div>'
        +'<button onclick="saveRoute()" style="width:100%;padding:14px;background:#024550;color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:700;cursor:pointer;font-family:inherit">'+(currentRouteId?'Uppdatera rutt':'Spara rutt')+'</button>'
        +'</div></div>';

    document.body.appendChild(modal);
}

async function saveRoute(){
    const name = document.getElementById('routeName').value.trim();
    const date = document.getElementById('routeDate').value;
    const assignee = document.getElementById('routeAssignee').value;
    const notes = document.getElementById('routeNotes').value.trim();
    // Använd inloggades staff_id (gStaffId sätts av login.js + sparas i sessionStorage)
    const myId = parseInt(window.gStaffId || sessionStorage.getItem('gStaffId') || 0, 10);
    if(!myId){ alert('Kunde inte hitta din användar-ID. Logga ut och in igen.'); return; }

    const body = {
        name: name || 'Rutt ' + date,
        created_by: myId,
        assigned_to: assignee ? parseInt(assignee) : myId,
        status: 'active',
        prospects: faltProspects,
        notes: notes || null,
        route_date: date
    };
    if(currentRouteId) body.id = currentRouteId;

    try {
        const res = await fetch('/api/routes.php', {
            method: 'POST',
            headers: {'Content-Type':'application/json'},
            body: JSON.stringify(body)
        });
        const data = await res.json();
        if(data.success){
            currentRouteId = data.id || currentRouteId;
            document.getElementById('routeSaveModal')?.remove();
            updateRouteInfoBar();
            const assigneeName = assignee ? (staffList.find(s=>s.id==assignee)?.name||'') : 'dig';
            alert('Rutt sparad! '+(assignee && assignee != myId ? 'Tilldelad till '+assigneeName+'.' : ''));
        } else {
            alert('Fel: '+(data.error||'okänt'));
        }
    } catch(e){
        alert('Fel: '+e.message);
    }
}

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

    modal.innerHTML = '<div style="background:#fff;border-radius:16px;max-width:600px;width:100%;max-height:85vh;display:flex;flex-direction:column;box-shadow:0 25px 60px rgba(0,0,0,.3)">'
        +'<div style="padding:20px 24px;border-bottom:1px solid #f1f5f9;display:flex;justify-content:space-between;align-items:center;flex-shrink:0">'
        +'<h2 style="font-size:18px;font-weight:700;color:#1a1a1a;margin:0">Sparade rutter</h2>'
        +'<button onclick="this.closest(\'#routeLoadModal\').remove()" style="background:none;border:none;cursor:pointer;padding:4px"><svg viewBox="0 0 24 24" style="width:20px;height:20px;stroke:#94a3b8;fill:none;stroke-width:2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>'
        +'</div>'
        +'<div id="routeListContent" style="padding:24px;overflow-y:auto;flex:1">'
        +'<div style="text-align:center;padding:20px;color:#94a3b8"><div class="spinner" style="display:inline-block;width:24px;height:24px;border:3px solid #e5e7eb;border-top-color:#024550;border-radius:50%;animation:bgspin .6s linear infinite"></div><p style="margin-top:8px;font-size:13px">Laddar rutter...</p></div>'
        +'</div></div>';

    document.body.appendChild(modal);

    // Fetch routes — scope styrs av rollens 'faltsalj.routes.scope' (default: own för icke-admin, all för admin)
    try {
        var _myStaffId = parseInt(window.gStaffId || sessionStorage.getItem('gStaffId') || 0, 10);
        var _routesScope = (typeof getRoleSetting === 'function')
            ? getRoleSetting('faltsalj.routes.scope')
            : (['admin','systemadmin','saljchef'].indexOf(window.gUserRole) >= 0 ? 'all' : 'own');
        var _routesUrl = (_routesScope === 'all') ? '/api/routes.php' : '/api/routes.php?staff_id=' + _myStaffId;
        const res = await fetch(_routesUrl);
        const data = await res.json();
        const container = document.getElementById('routeListContent');
        if(!data.success || !data.routes || data.routes.length === 0){
            container.innerHTML = '<div style="text-align:center;padding:30px;color:#94a3b8"><svg viewBox="0 0 24 24" style="width:40px;height:40px;stroke:currentColor;fill:none;stroke-width:1.5;margin-bottom:8px"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg><p style="font-size:14px;font-weight:600">Inga sparade rutter</p><p style="font-size:12px">Spara din nuvarande rutt med knappen "Spara rutt"</p></div>';
            return;
        }

        const statusColors = {draft:'#94a3b8',active:'#10b981',completed:'#3b82f6',archived:'#64748b'};
        const statusLabels = {draft:'Utkast',active:'Aktiv',completed:'Avklarad',archived:'Arkiverad'};

        container.innerHTML = data.routes.map(r => {
            const count = r.prospect_count || 0;
            const col = statusColors[r.status]||'#94a3b8';
            return '<div style="border:1px solid #e5e7eb;border-radius:12px;padding:16px;margin-bottom:10px;cursor:pointer;transition:all .15s" onmouseover="this.style.borderColor=\'#3b82f6\';this.style.background=\'#f0f9ff\'" onmouseout="this.style.borderColor=\'#e5e7eb\';this.style.background=\'#fff\'">'
                +'<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:8px">'
                +'<div>'
                +'<div style="font-weight:700;font-size:15px;color:#1a1a1a">'+r.name+'</div>'
                +'<div style="font-size:12px;color:#64748b;margin-top:2px">'+(r.route_date||r.created_at?.split(' ')[0]||'')+'</div>'
                +'</div>'
                +'<span style="background:'+col+'20;color:'+col+';font-size:10px;font-weight:700;padding:3px 8px;border-radius:6px;text-transform:uppercase">'+(statusLabels[r.status]||r.status)+'</span>'
                +'</div>'
                +'<div style="display:flex;gap:16px;font-size:12px;color:#64748b;margin-bottom:10px">'
                +'<span>'+count+' prospekt</span>'
                +(r.assigned_to_name ? '<span>Tilldelad: <strong>'+r.assigned_to_name+'</strong></span>' : '')
                +(r.created_by_name ? '<span>Skapad av: '+r.created_by_name+'</span>' : '')
                +'</div>'
                +(r.notes ? '<div style="font-size:12px;color:#64748b;margin-bottom:10px;font-style:italic">'+r.notes+'</div>' : '')
                +'<div style="display:flex;gap:8px">'
                +'<button onclick="loadRoute('+r.id+')" style="flex:1;padding:8px;background:#024550;color:#fff;border:none;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">Ladda rutt</button>'
                +'<button onclick="deleteRoute('+r.id+')" style="padding:8px 12px;background:#fee2e2;color:#dc2626;border:none;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit">Ta bort</button>'
                +'</div>'
                +'</div>';
        }).join('');
    } catch(e){
        document.getElementById('routeListContent').innerHTML = '<div style="padding:20px;color:#ef4444;text-align:center">Fel: '+e.message+'</div>';
    }
}

async function loadRoute(id){
    try {
        const res = await fetch('/api/routes.php?id='+id);
        const data = await res.json();
        if(!data.success || !data.route){
            alert('Kunde inte ladda rutt');
            return;
        }
        const route = data.route;
        faltProspects = route.prospects || [];
        currentRouteId = route.id;
        _persistFaltProspects();

        document.getElementById('routeLoadModal')?.remove();

        renderFaltMarkers();
        renderFaltList();
        updateFaltStats();
        renderLeadsTable(); try { if (typeof renderLeadsPage === 'function') renderLeadsPage(true); } catch(e){}
        updateRouteInfoBar();

        // Zoom to fit all prospects
        if(gMap && faltProspects.length > 0){
            const bounds = new google.maps.LatLngBounds();
            faltProspects.forEach(p => bounds.extend({lat:p.lat,lng:p.lng}));
            gMap.fitBounds(bounds, 50);
        }
    } catch(e){
        alert('Fel: '+e.message);
    }
}

async function deleteRoute(id){
    if(!confirm('Ta bort denna sparade rutt?')) return;
    try {
        const res = await fetch('/api/routes.php?id='+id, {method:'DELETE'});
        const data = await res.json();
        if(data.success){
            if(currentRouteId === id) { currentRouteId = null; updateRouteInfoBar(); }
            document.getElementById('routeLoadModal')?.remove();
            showLoadRouteModal(); // refresh list
        }
    } catch(e){ alert('Fel: '+e.message); }
}