// 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">✓</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); }
}