js/settings.js

Code: DEV-0070BF18 Size: 24.5 KB Lines: 472 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/js/settings.js

Task / Comment

Open report form
// settings.js - Settings, Google keys, Monday, valuta

function switchSettingsTab(tab) {
    document.querySelectorAll('.settings-tab').forEach(t => t.classList.remove('active'));
    document.querySelectorAll('.settings-panel').forEach(p => p.classList.remove('active'));
    document.querySelector('[data-settings-tab="' + tab + '"]').classList.add('active');
    document.getElementById('settings-' + tab).classList.add('active');
    if (tab === 'google') loadGoogleSettings();
    if (tab === 'ai') loadAiSettings();
    if (tab === 'monday') loadMondaySettings(); if (tab === 'valuta') loadCurrencySettings();
    if (tab === 'foretag') loadCompanySettings();
}

// === COMPANY SETTINGS ===
const COMPANY_KEYS = ['company_name','company_orgnr','company_address','company_phone','company_email','company_website','company_footer','company_logo'];

async function loadCompanySettings() {
    try {
        const res = await fetch('/api/settings.php?keys=' + COMPANY_KEYS.join(','));
        const data = await res.json();
        const s = (data && data.settings) || {};
        const setVal = (id, key) => { const el = document.getElementById(id); if (el) el.value = s[key] || ''; };
        setVal('settCompanyName','company_name');
        setVal('settCompanyOrgnr','company_orgnr');
        setVal('settCompanyAddress','company_address');
        setVal('settCompanyPhone','company_phone');
        setVal('settCompanyEmail','company_email');
        setVal('settCompanyWebsite','company_website');
        setVal('settCompanyFooter','company_footer');
        _updateLogoPreview(s.company_logo || '');
    } catch(e) {}
}

function _updateLogoPreview(url) {
    const box = document.getElementById('companyLogoPreview');
    const removeBtn = document.getElementById('companyLogoRemoveBtn');
    if (!box) return;
    if (url) {
        box.innerHTML = '<img src="' + url + '?t=' + Date.now() + '" alt="Logo" style="max-width:100%;max-height:100%;object-fit:contain">';
        if (removeBtn) removeBtn.style.display = '';
    } else {
        box.innerHTML = '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>';
        if (removeBtn) removeBtn.style.display = 'none';
    }
}

async function saveCompanySettings() {
    const msg = document.getElementById('companySettingsMsg');
    if (msg) msg.innerHTML = '<span style="color:#64748b">Sparar...</span>';
    const settings = {
        company_name: document.getElementById('settCompanyName').value.trim(),
        company_orgnr: document.getElementById('settCompanyOrgnr').value.trim(),
        company_address: document.getElementById('settCompanyAddress').value.trim(),
        company_phone: document.getElementById('settCompanyPhone').value.trim(),
        company_email: document.getElementById('settCompanyEmail').value.trim(),
        company_website: document.getElementById('settCompanyWebsite').value.trim(),
        company_footer: document.getElementById('settCompanyFooter').value.trim()
    };
    try {
        const res = await fetch('/api/settings.php', {
            method: 'POST',
            headers: {'Content-Type':'application/json'},
            body: JSON.stringify({ settings })
        });
        const data = await res.json();
        if (data.success) {
            if (msg) { msg.innerHTML = '<span style="color:#10b981;font-weight:600">Sparat!</span>'; setTimeout(() => msg.textContent = '', 3000); }
        } else {
            if (msg) msg.innerHTML = '<span style="color:#ef4444">' + (data.error || 'Fel') + '</span>';
        }
    } catch(e) {
        if (msg) msg.innerHTML = '<span style="color:#ef4444">Nätverksfel</span>';
    }
}

async function uploadCompanyLogo(file) {
    const msg = document.getElementById('companyLogoMsg');
    if (!file) return;
    if (file.size > 2 * 1024 * 1024) {
        if (msg) msg.innerHTML = '<span style="color:#ef4444">Filen är för stor (max 2 MB)</span>';
        return;
    }
    if (msg) msg.innerHTML = '<span style="color:#64748b">Laddar upp...</span>';
    const fd = new FormData();
    fd.append('logo', file);
    try {
        const res = await fetch('/api/company_logo.php', { method:'POST', body: fd });
        const data = await res.json();
        if (data && data.success && data.url) {
            _updateLogoPreview(data.url);
            if (msg) { msg.innerHTML = '<span style="color:#10b981;font-weight:600">Uppladdad!</span>'; setTimeout(() => msg.textContent = '', 3000); }
        } else {
            if (msg) msg.innerHTML = '<span style="color:#ef4444">' + ((data && data.error) || 'Fel vid uppladdning') + '</span>';
        }
    } catch(e) {
        if (msg) msg.innerHTML = '<span style="color:#ef4444">Nätverksfel: ' + e.message + '</span>';
    }
    const input = document.getElementById('companyLogoInput');
    if (input) input.value = '';
}

async function removeCompanyLogo() {
    if (!confirm('Ta bort logotypen?')) return;
    try {
        await fetch('/api/settings.php', {
            method:'POST',
            headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ settings: { company_logo: '' } })
        });
        _updateLogoPreview('');
        const msg = document.getElementById('companyLogoMsg');
        if (msg) { msg.innerHTML = '<span style="color:#10b981;font-weight:600">Borttagen</span>'; setTimeout(() => msg.textContent = '', 3000); }
    } catch(e) {}
}

window.saveCompanySettings = saveCompanySettings;
window.uploadCompanyLogo = uploadCompanyLogo;
window.removeCompanyLogo = removeCompanyLogo;

// === GOOGLE SETTINGS ===
async function loadGoogleSettings() {
    const uriEl = document.getElementById('googleRedirectUri');
    if (uriEl) uriEl.textContent = window.location.origin;
    try {
        const res = await fetch('/api/settings.php?keys=google_client_id,google_client_secret,google_maps_key');
        const data = await res.json();
        if (data.settings) {
            document.getElementById('settGoogleClientId').value = data.settings.google_client_id || '';
            document.getElementById('settGoogleClientSecret').value = data.settings.google_client_secret_set ? '••••••••' : '';
            if (data.settings.google_client_secret_set) {
                document.getElementById('settGoogleSecretStatus').textContent = 'Konfigurerad';
            }
            document.getElementById('settGoogleMapsKey').value = data.settings.google_maps_key || '';
        }
    } catch(e) {}
}

async function saveGoogleSettings() {
    const settings = {};
    const clientId = document.getElementById('settGoogleClientId').value.trim();
    const secret = document.getElementById('settGoogleClientSecret').value.trim();
    const mapsKey = document.getElementById('settGoogleMapsKey').value.trim();
    if (clientId) settings.google_client_id = clientId;
    if (secret && !secret.startsWith('••')) settings.google_client_secret = secret;
    if (mapsKey) settings.google_maps_key = mapsKey;
    try {
        const res = await fetch('/api/settings.php', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({settings})
        });
        const data = await res.json();
        const msg = document.getElementById('googleSettingsMsg');
        if (data.success) {
            msg.innerHTML = '<span style="color:#10b981;font-weight:600">Sparat!</span>';
            setTimeout(() => msg.textContent = '', 3000);
        } else {
            msg.innerHTML = '<span style="color:#ef4444">' + (data.error || 'Fel') + '</span>';
        }
    } catch(e) {
        document.getElementById('googleSettingsMsg').innerHTML = '<span style="color:#ef4444">Nätverksfel</span>';
    }
}

// === GOOGLE OAUTH + GMAIL + CALENDAR ===
const GOOGLE_SCOPES = 'openid email profile https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly';
const LOGIN_SCOPES = 'openid email profile';

async function initGoogleAuth() {
    // Load GIS library if not present
    if (!window.google?.accounts?.oauth2) {
        const script = document.createElement('script');
        script.src = 'https://accounts.google.com/gsi/client';
        script.async = true;
        document.head.appendChild(script);
        await new Promise(r => script.onload = r);
    }
    // Get client ID from settings
    try {
        const res = await fetch('/api/settings.php?keys=google_client_id');
        const data = await res.json();
        return data.settings?.google_client_id || '';
    } catch(e) { return ''; }
}

async function googleLogin() {
    const clientId = await initGoogleAuth();
    if (!clientId) {
        alert('Google Client ID inte konfigurerat. Gå till Inställningar → Google API.');
        return;
    }
    const client = google.accounts.oauth2.initCodeClient({
        client_id: clientId,
        scope: GOOGLE_SCOPES,
        ux_mode: 'popup',
        redirect_uri: window.location.origin,
        callback: async (response) => {
            if (response.error) { alert('Google login misslyckades: ' + response.error); return; }
            try {
                const res = await fetch('/api/google-token.php', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        action: 'exchange',
                        code: response.code,
                        redirectUri: window.location.origin
                    })
                });
                const data = await res.json();
                if (data.error) { const errEl = document.getElementById("loginError"); const pendEl = document.getElementById("loginPending"); if(errEl) errEl.style.display="none"; if(pendEl) pendEl.style.display="none"; if(data.pending) { if(pendEl){ pendEl.textContent=data.error; pendEl.style.display="block"; } } else { if(errEl){ errEl.textContent=data.error; errEl.style.display="block"; } } return; }
                gAccessToken = data.accessToken;
                gTokenExpiry = Date.now() + (data.expiresIn * 1000);
                gStaffId = data.user.id;
                gUserEmail = data.user.email;
                gUserName = data.user.name;
                gUserAvatar = data.user.avatar_url || '';
                gUserRole = data.user.role || '';
                gMailSignature = data.user.mail_signature || '';
                sessionStorage.setItem('gAccessToken', gAccessToken);
                sessionStorage.setItem('gTokenExpiry', gTokenExpiry);
                sessionStorage.setItem('gStaffId', gStaffId);
                sessionStorage.setItem('gUserEmail', gUserEmail);
                sessionStorage.setItem('gUserName', gUserName);
                sessionStorage.setItem('gUserAvatar', gUserAvatar);
                sessionStorage.setItem('gUserRole', gUserRole);
                sessionStorage.setItem('gmailReady', '1');
                localStorage.setItem('mailSignature', gMailSignature);
                updateAuthUI();
                // Auto-load mail and calendar
                if (document.getElementById('page-inkorg').classList.contains('active')) loadGmailInbox();
                if (document.getElementById('page-kalender').classList.contains('active')) loadCalendarEvents();
            } catch(e) { alert('Anslutningsfel: ' + e.message); }
        }
    });
    client.requestCode();
}

async function ensureToken() {
    if (!gAccessToken) return false;
    if (Date.now() > gTokenExpiry - 60000) {
        // Token expired or about to expire, refresh
        try {
            const res = await fetch('/api/google-token.php', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ action: 'refresh', staffId: gStaffId })
            });
            const data = await res.json();
            if (data.accessToken) {
                gAccessToken = data.accessToken;
                gTokenExpiry = Date.now() + (data.expiresIn * 1000);
                sessionStorage.setItem('gAccessToken', gAccessToken);
                sessionStorage.setItem('gTokenExpiry', gTokenExpiry);
                return true;
            }
        } catch(e) {}
        return false;
    }
    return true;
}

function updateAuthUI() {
    const loginRequired = !gAccessToken;
    // Mail page
    const mailLogin = document.getElementById('mailLogin');
    const mailClient = document.getElementById('mailClient');
    if (loginRequired) {
        if (mailLogin) { mailLogin.style.display = 'block'; mailLogin.innerHTML = '<div style="text-align:center;padding:40px"><p style="font-size:16px;color:#334155;margin-bottom:16px">Logga in med Google för att ansluta din e-post och kalender</p><button onclick="googleLogin()" style="padding:12px 28px;background:#024550;color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;font-family:inherit">Logga in med Google</button></div>'; }
        if (mailClient) mailClient.style.display = 'none';
    } else {
        if (mailLogin) mailLogin.style.display = 'none';
        if (mailClient) mailClient.style.display = 'block';
    }
    // Calendar page
    const calSetup = document.getElementById('calSetup');
    if (calSetup) {
        if (loginRequired) {
            calSetup.innerHTML = '<div style="text-align:center;padding:20px"><p style="font-size:16px;color:#334155;margin-bottom:16px">Logga in med Google för att visa din kalender</p><button onclick="googleLogin()" style="padding:12px 28px;background:#024550;color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;font-family:inherit">Logga in med Google</button></div>';
        } else {
            calSetup.innerHTML = '<div style="display:flex;align-items:center;gap:12px"><div style="width:36px;height:36px;border-radius:50%;background:#f0fdf4;display:flex;align-items:center;justify-content:center"><span style="color:#10b981;font-size:18px">&#10003;</span></div><div><strong style="font-size:14px">' + gUserName + '</strong><br><span style="font-size:12px;color:#64748b">' + gUserEmail + '</span></div></div>';
        }
    }
}

async function loadMondaySettings(){
    try {
        const res = await fetch('/api/settings.php?keys=monday_api_token');
        const data = await res.json();
        const token = data.settings?.monday_api_token || '';
        const input = document.getElementById('settMondayToken');
        if(input && token) input.value = token;
        if(token) loadMondayBoards();
    } catch(e){}
}

async function testMondayConnection(){
    const statusEl = document.getElementById('mondayConnectionStatus');
    statusEl.innerHTML = '<span style="color:#64748b">Testar...</span>';
    try {
        const res = await fetch('/api/monday.php?action=test');
        const data = await res.json();
        if(data.success){
            statusEl.innerHTML = '<span style="color:#059669">Ansluten som <strong>'+data.user.name+'</strong> ('+data.user.email+')</span>';
            loadMondayBoards();
        } else {
            statusEl.innerHTML = '<span style="color:#ef4444">'+( data.error||'Anslutning misslyckades')+'</span>';
        }
    } catch(e){
        statusEl.innerHTML = '<span style="color:#ef4444">Fel: '+e.message+'</span>';
    }
}

async function saveMondaySettings(){
    const token = document.getElementById('settMondayToken').value;
    try {
        await fetch('/api/settings.php', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({monday_api_token: token})
        });
        testMondayConnection();
    } catch(e){ alert('Kunde inte spara: '+e.message); }
}

// === VALUTA ===
let _currencyRates = {};

async function loadCurrencySettings() {
    try {
        const res = await fetch('/api/settings.php?keys=currency_EUR,currency_USD,currency_DKK,currency_NOK,currency_updated');
        const data = await res.json();
        if(data.success) {
            const s = data.settings;
            if(s.currency_EUR) { _currencyRates.EUR = parseFloat(s.currency_EUR); var _el=document.getElementById('settCurrencyEUR'); if(_el) _el.value=s.currency_EUR; }
            if(s.currency_USD) { _currencyRates.USD = parseFloat(s.currency_USD); var _el=document.getElementById('settCurrencyUSD'); if(_el) _el.value=s.currency_USD; }
            if(s.currency_DKK) { _currencyRates.DKK = parseFloat(s.currency_DKK); var _el=document.getElementById('settCurrencyDKK'); if(_el) _el.value=s.currency_DKK; }
            if(s.currency_NOK) { _currencyRates.NOK = parseFloat(s.currency_NOK); var _el=document.getElementById('settCurrencyNOK'); if(_el) _el.value=s.currency_NOK; }
            if(s.currency_updated) {
                const el = document.getElementById('eurRateInfo');
                if(el) el.textContent = 'Senast uppdaterad: ' + s.currency_updated;
            }
        }
    } catch(e) { console.error('loadCurrencySettings error', e); }
}

async function saveCurrencySettings() {
    const eur = document.getElementById('settCurrencyEUR').value;
    const usd = document.getElementById('settCurrencyUSD').value;
    const dkk = document.getElementById('settCurrencyDKK').value;
    const nok = document.getElementById('settCurrencyNOK').value;
    const status = document.getElementById('currencySaveStatus');
    try {
        const res = await fetch('/api/settings.php', {
            method: 'POST', headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({ settings: {
                currency_EUR: eur, currency_USD: usd, currency_DKK: dkk, currency_NOK: nok,
                currency_updated: new Date().toISOString().slice(0,16).replace('T',' ')
            }})
        });
        const data = await res.json();
        if(data.success) {
            _currencyRates = { EUR: parseFloat(eur)||0, USD: parseFloat(usd)||0, DKK: parseFloat(dkk)||0, NOK: parseFloat(nok)||0 };
            status.innerHTML = '<span style="color:#059669">\u2714 Sparat!</span>';
            setTimeout(() => status.textContent = '', 3000);
        }
    } catch(e) { status.innerHTML = '<span style="color:#dc2626">Fel: '+e.message+'</span>'; }
}

async function fetchLiveRates() {
    const status = document.getElementById('currencySaveStatus');
    status.innerHTML = '<span style="color:#0284c7">Hämtar kurser...</span>';
    try {
        const res = await fetch('https://api.frankfurter.dev/v1/latest?base=SEK&symbols=EUR,USD,DKK,NOK');
        const data = await res.json();
        if(data.rates) {
            // API ger SEK->X, vi vill ha X->SEK (1/rate)
            if(data.rates.EUR) document.getElementById('settCurrencyEUR').value = (1/data.rates.EUR).toFixed(4);
            if(data.rates.USD) document.getElementById('settCurrencyUSD').value = (1/data.rates.USD).toFixed(4);
            if(data.rates.DKK) document.getElementById('settCurrencyDKK').value = (1/data.rates.DKK).toFixed(4);
            if(data.rates.NOK) document.getElementById('settCurrencyNOK').value = (1/data.rates.NOK).toFixed(4);
            status.innerHTML = '<span style="color:#059669">\u2714 Kurser hämtade (Frankfurter API). Klicka Spara!</span>';
        }
    } catch(e) { status.innerHTML = '<span style="color:#dc2626">Kunde inte hämta: '+e.message+'</span>'; }
}

function convertToSEK(amount, currency) {
    if(!currency || currency === 'SEK') return amount;
    const rate = _currencyRates[currency];
    return rate ? amount * rate : amount;
}

async function loadMondayBoards(){
    const section = document.getElementById('mondayBoardsSection');
    const list = document.getElementById('mondayBoardsList');
    try {
        const res = await fetch('/api/monday.php?action=boards');
        const boards = await res.json();
        if(boards.length === 0){ section.style.display='none'; return; }
        section.style.display='';
        list.innerHTML = boards.filter(b => b.state === 'active').map(b => {
            const kind = b.board_kind === 'private' ? ' (privat)' : '';
            return '<div style="padding:10px 14px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;display:flex;justify-content:space-between;align-items:center">'
                +'<div><span style="font-size:14px;font-weight:500;color:#1a1a1a">'+b.name+'</span>'
                +'<span style="font-size:11px;color:#94a3b8;margin-left:8px">'+b.items_count+' items'+kind+'</span></div>'
                +'<button onclick="viewMondayBoard('+b.id+')" style="padding:4px 12px;background:#f1f5f9;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;cursor:pointer;font-family:inherit;color:#334155">Visa</button>'
                +'</div>';
        }).join('');
    } catch(e){ console.error('loadMondayBoards error', e); }
}

async function viewMondayBoard(boardId){
    try {
        const res = await fetch('/api/monday.php?action=board&id='+boardId);
        const board = await res.json();
        if(!board) return;
        let html = '<h3 style="margin:24px 0 8px">'+board.name+'</h3>';
        html += '<p style="font-size:12px;color:#94a3b8;margin-bottom:12px">'+board.columns.length+' kolumner, '+board.groups.length+' grupper</p>';
        html += '<div style="margin-bottom:16px"><strong style="font-size:13px">Grupper:</strong> <span style="font-size:13px;color:#64748b">'+board.groups.map(g=>g.title).join(', ')+'</span></div>';
        html += '<table style="width:100%;border-collapse:collapse;font-size:12px;margin-bottom:16px"><thead><tr style="background:#f8fafc"><th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e5e7eb">Kolumn</th><th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e5e7eb">Typ</th></tr></thead><tbody>';
        board.columns.forEach(c => {
            html += '<tr><td style="padding:4px 10px;border-bottom:1px solid #f1f5f9">'+c.title+'</td><td style="padding:4px 10px;border-bottom:1px solid #f1f5f9;color:#64748b">'+c.type+'</td></tr>';
        });
        html += '</tbody></table>';
        if(board.items_page && board.items_page.items && board.items_page.items.length > 0){
            html += '<strong style="font-size:13px">Senaste items ('+board.items_page.items.length+'):</strong>';
            html += '<div style="display:flex;flex-direction:column;gap:4px;margin-top:8px">';
            board.items_page.items.slice(0,10).forEach(item => {
                html += '<div style="padding:6px 10px;background:#fafbfc;border-radius:6px;font-size:12px;color:#1a1a1a">'+item.name+'</div>';
            });
            if(board.items_page.items.length > 10) html += '<div style="font-size:11px;color:#94a3b8;padding:4px 10px">...och '+(board.items_page.items.length-10)+' till</div>';
            html += '</div>';
        }
        document.getElementById('mondayBoardsList').insertAdjacentHTML('beforeend', html);
    } catch(e){ alert('Kunde inte ladda board: '+e.message); }
}


// AI Settings
async function loadAiSettings(){
    try {
        const res = await fetch('/api/settings.php?keys=openai_api_key,openai_model,gemini_api_key,gemini_model,default_ai_provider');
        const data = await res.json();
        const s = data.settings || {};
        if(s.openai_api_key) document.getElementById('settOpenaiKey').value = s.openai_api_key;
        if(s.openai_model) document.getElementById('settOpenaiModel').value = s.openai_model;
        if(s.gemini_api_key) document.getElementById('settGeminiKey').value = s.gemini_api_key;
        // gemini_model is fixed: gemini-2.5-flash-image
        if(s.default_ai_provider) document.getElementById('settDefaultProvider').value = s.default_ai_provider;
    } catch(e){ console.error('loadAiSettings', e); }
}

async function saveAiSettings(){
    const settings = {
        openai_api_key: document.getElementById('settOpenaiKey').value,
        openai_model: document.getElementById('settOpenaiModel').value,
        gemini_api_key: document.getElementById('settGeminiKey').value,
        gemini_model: 'gemini-2.5-flash-image',
        default_ai_provider: document.getElementById('settDefaultProvider').value,
    };
    try {
        await fetch('/api/settings.php', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify(settings)
        });
        document.getElementById('openaiTestStatus').innerHTML = '<span style="color:#059669">Sparad</span>';
        document.getElementById('geminiTestStatus').innerHTML = '<span style="color:#059669">Sparad</span>';
        setTimeout(() => {
            document.getElementById('openaiTestStatus').innerHTML = '';
            document.getElementById('geminiTestStatus').innerHTML = '';
        }, 2000);
    } catch(e){ alert('Fel: '+e.message); }
}