// admin.js - Admin, permissions, pending users
function updateAdminVisibility(){
const navAdmin = document.getElementById('navAdmin');
if(navAdmin){
navAdmin.style.display = (gUserRole === 'systemadmin' || gUserRole === 'admin' || gUserRole === 'saljchef') ? '' : 'none';
}
}
// Admin-sida
function switchAdminTab(tab){
document.querySelectorAll('[data-admin-tab]').forEach(t => {
const isActive = t.dataset.adminTab === tab;
t.style.borderBottomColor = isActive ? '#024550' : 'transparent';
t.style.color = isActive ? '#024550' : '#64748b';
});
document.getElementById('adminUsersPanel').style.display = tab==='users' ? '' : 'none';
document.getElementById('adminPendingPanel').style.display = tab==='pending' ? '' : 'none';
document.getElementById('adminPermissionsPanel').style.display = tab==='permissions' ? '' : 'none';
if(tab==='users') loadAdminUsers();
if(tab==='pending') loadAdminPending();
if(tab==='permissions') loadPermissions();
}
const PAGE_LABELS = {oversikt:'Dashboard',faltsalj:'FältSälj',leads:'Leads',kunder:'Kunder',produkter:'Produktkatalog',konfigurator:'Kalkyler',projekt:'Affärer',projektering:'Projektflöde',ue:'Entreprenörer',inkop:'Inköp',leverantorer:'Leverantörer',ekonomi:'Ekonomi',bildgen:'Bildgenerering',personal:'Personal',kalender:'Kalender',inkorg:'Inkorg',dagrapport:'Dagrapport','my-salary':'My Salary',profile:'Profile',settings:'Inställningar'};
const PAGE_KEYS = Object.keys(PAGE_LABELS);
const PERM_ROLES = ['admin','saljchef','saljare','installator','projektledare','ekonomi'];
const PAGE_SETTINGS = {
oversikt: {
label: 'Widgets',
items: [
{ label: 'Totalt Värde', visibleKey: 'dashboard.widget.stat_total.visible', scopeKey: 'dashboard.widget.stat_total.scope' },
{ label: 'Pågående Offerter', visibleKey: 'dashboard.widget.stat_offertar.visible', scopeKey: 'dashboard.widget.stat_offertar.scope' },
{ label: 'Kunder', visibleKey: 'dashboard.widget.stat_kunder.visible', scopeKey: 'dashboard.widget.stat_kunder.scope' },
{ label: 'Accepterade', visibleKey: 'dashboard.widget.stat_accepted.visible', scopeKey: 'dashboard.widget.stat_accepted.scope' },
{ label: 'Top Säljare', visibleKey: 'dashboard.widget.top_sellers.visible' },
{ label: 'Senaste Affärer', visibleKey: 'dashboard.widget.recent_deals.visible', scopeKey: 'dashboard.widget.recent_deals.scope' },
{ label: 'Kommande Möten', visibleKey: 'dashboard.widget.meetings.visible' },
{ label: 'Försäljning (månad)', visibleKey: 'dashboard.widget.chart_monthly.visible', scopeKey: 'dashboard.widget.chart_monthly.scope' },
{ label: 'Helårsöversikt', visibleKey: 'dashboard.widget.chart_yearly.visible', scopeKey: 'dashboard.widget.chart_yearly.scope' },
]
},
kunder: { label: 'Inställningar', items: [{ label: 'Visa kunder', scopeKey: 'kunder.scope' }] },
faltsalj: { label: 'Inställningar', items: [{ label: 'Visa rutter', scopeKey: 'faltsalj.routes.scope' }] },
konfigurator: { label: 'Inställningar', items: [
{ label: 'Visa kalkyler', scopeKey: 'kalkyler.scope' },
{ label: 'Visa priser', toggleKey: 'kalkyler.show_prices' },
{ label: 'Visa CalcBuilder', toggleKey: 'kalkyler.show_calcbuilder' }
]},
produkter: { label: 'Inställningar', items: [{ label: 'Visa priser', toggleKey: 'produkter.show_prices' }] }
};
function permDefaultScope(role){
return ['admin','systemadmin','saljchef','ekonomi'].indexOf(role) >= 0 ? 'all' : 'own';
}
function permGetSetting(role, key){
const r = permSettings[role] || {};
if(r[key] !== undefined) return r[key];
if(key.endsWith('.scope')) return permDefaultScope(role);
return '1';
}
let permData = {};
let permSettings = {};
let permExpandedPages = {};
let permSelectedRole = 'saljare';
var menuOrder = [];
async function loadPermissions(){
try {
const [permRes, settRes] = await Promise.all([
fetch('/api/permissions.php'),
fetch('/api/permissions.php?action=settings')
]);
permData = await permRes.json();
permSettings = await settRes.json();
renderPermRoleTabs();
renderPermMatrix();
await loadMenuOrder();
} catch(e){ console.error('loadPermissions error', e); }
}
async function loadMenuOrder(){
try {
const res = await fetch('/api/permissions.php?action=order&t='+Date.now());
menuOrder = await res.json();
renderMenuOrder();
} catch(e){ console.error('loadMenuOrder error', e); }
}
function renderMenuOrder(){
const container = document.getElementById('menuOrderList');
if(!container) return;
container.innerHTML = menuOrder.map((key, i) => {
const label = PAGE_LABELS[key] || key;
return `<div draggable="true" data-page-key="${key}" style="padding:8px 12px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;font-size:13px;color:#1a1a1a;cursor:grab;display:flex;align-items:center;gap:8px;user-select:none;transition:box-shadow .15s" onmousedown="this.style.cursor='grabbing'" onmouseup="this.style.cursor='grab'"><svg viewBox="0 0 24 24" style="width:14px;height:14px;stroke:#94a3b8;fill:none;stroke-width:2;flex-shrink:0"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>${label}</div>`;
}).join('');
// Add drag events
const items = container.querySelectorAll('[draggable]');
items.forEach(item => {
item.addEventListener('dragstart', e => {
e.dataTransfer.setData('text/plain', item.dataset.pageKey);
item.style.opacity = '0.4';
});
item.addEventListener('dragend', e => { item.style.opacity = '1'; });
item.addEventListener('dragover', e => {
e.preventDefault();
item.style.boxShadow = '0 -2px 0 #024550';
});
item.addEventListener('dragleave', e => { item.style.boxShadow = ''; });
item.addEventListener('drop', async e => {
e.preventDefault();
item.style.boxShadow = '';
const draggedKey = e.dataTransfer.getData('text/plain');
const targetKey = item.dataset.pageKey;
if(draggedKey === targetKey) return;
const oldIdx = menuOrder.indexOf(draggedKey);
const newIdx = menuOrder.indexOf(targetKey);
menuOrder.splice(oldIdx, 1);
menuOrder.splice(newIdx, 0, draggedKey);
renderMenuOrder();
await saveMenuOrder();
applyNavOrder();
});
});
}
async function saveMenuOrder(){
try {
await fetch('/api/permissions.php?action=order', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ order: menuOrder })
});
} catch(e){ console.error('saveMenuOrder error', e); }
}
function applyNavOrder(){
const nav = document.querySelector('.sidebar-nav');
if(!nav) return;
const items = Array.from(nav.querySelectorAll('.nav-item[data-page]'));
const adminItem = items.find(el => el.dataset.page === 'admin');
items.sort((a, b) => {
const ai = menuOrder.indexOf(a.dataset.page);
const bi = menuOrder.indexOf(b.dataset.page);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
items.forEach(item => nav.appendChild(item));
if(adminItem) nav.appendChild(adminItem);
}
function renderPermRoleTabs(){
const container = document.getElementById('permRoleTabs');
container.innerHTML = PERM_ROLES.map(r => {
const active = r === permSelectedRole;
return `<button onclick="selectPermRole('${r}')" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:${active?'600':'500'};cursor:pointer;font-family:inherit;border:1.5px solid ${active?'#024550':'#e5e7eb'};background:${active?'#024550':'#fff'};color:${active?'#fff':'#334155'};transition:all .15s">${ROLE_LABELS[r]}</button>`;
}).join('');
}
function selectPermRole(role){
permSelectedRole = role;
renderPermRoleTabs();
renderPermMatrix();
}
function _permToggleHtml(checked, onchange){
return `<label style="position:relative;display:inline-block;width:44px;height:24px;cursor:pointer;flex-shrink:0">`
+`<input type="checkbox" ${checked?'checked':''} onchange="${onchange}" style="opacity:0;width:0;height:0">`
+`<span style="position:absolute;inset:0;background:${checked?'#10b981':'#e5e7eb'};border-radius:24px;transition:all .2s"></span>`
+`<span style="position:absolute;left:${checked?'22px':'2px'};top:2px;width:20px;height:20px;background:#fff;border-radius:50%;transition:all .2s;box-shadow:0 1px 3px rgba(0,0,0,.15)"></span>`
+`</label>`;
}
function _permScopeSelect(currentValue, onchange){
return `<select onchange="${onchange}" style="padding:5px 10px;border:1.5px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;background:#fff;cursor:pointer">`
+`<option value="own"${currentValue==='own'?' selected':''}>Egna</option>`
+`<option value="all"${currentValue==='all'?' selected':''}>Allas</option>`
+`</select>`;
}
function renderPermMatrix(){
const container = document.getElementById('permMatrixContainer');
const rolePerms = permData[permSelectedRole] || {};
let html = '<div style="border:1px solid #e5e7eb;border-radius:10px;overflow:hidden">';
html += '<div style="display:grid;grid-template-columns:1fr auto;padding:10px 16px;background:#f8fafc;border-bottom:1px solid #e5e7eb"><div style="font-weight:600;font-size:13px;color:#64748b">Menyval</div><div style="font-weight:600;font-size:13px;color:#64748b;text-align:center">Synlig</div></div>';
PAGE_KEYS.forEach((key, i) => {
const visible = rolePerms[key] !== undefined ? rolePerms[key] : 1;
const bg = i % 2 === 0 ? '#fff' : '#fafbfc';
const border = i < PAGE_KEYS.length - 1 ? 'border-bottom:1px solid #f1f5f9;' : '';
const hasSettings = !!PAGE_SETTINGS[key];
const expanded = !!permExpandedPages[key];
const chevron = hasSettings
? `<button onclick="togglePermPageExpand('${key}')" style="background:none;border:none;cursor:pointer;padding:2px;color:#64748b;display:flex;align-items:center;transform:rotate(${expanded?90:0}deg);transition:transform .15s"><svg viewBox="0 0 24 24" style="width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2"><polyline points="9 18 15 12 9 6"/></svg></button>`
: '<span style="width:18px;display:inline-block"></span>';
html += `<div style="display:grid;grid-template-columns:1fr auto;${border}background:${bg}">`;
html += `<div style="padding:10px 16px;font-size:14px;color:#1a1a1a;display:flex;align-items:center;gap:8px">${chevron}<span>${PAGE_LABELS[key]}</span></div>`;
html += `<div style="padding:10px 16px;text-align:center">${_permToggleHtml(!!visible, `togglePerm('${key}',this.checked)`)}</div>`;
html += `</div>`;
if(hasSettings && expanded){
html += renderPermSettingsPanel(key, bg, border);
}
});
html += '</div>';
container.innerHTML = html;
}
function renderPermSettingsPanel(pageKey, bg, border){
const cfg = PAGE_SETTINGS[pageKey];
let html = `<div style="${border}background:${bg};padding:8px 16px 14px 42px">`;
html += `<div style="font-size:11px;font-weight:600;color:#94a3b8;text-transform:uppercase;letter-spacing:.4px;margin-bottom:8px">${cfg.label}</div>`;
html += `<div style="display:flex;flex-direction:column;gap:6px">`;
cfg.items.forEach(item => {
html += `<div style="display:flex;align-items:center;gap:12px;padding:6px 10px;background:#fff;border:1px solid #f1f5f9;border-radius:8px">`;
html += `<div style="flex:1;font-size:13px;color:#334155">${item.label}</div>`;
if(item.visibleKey){
const v = permGetSetting(permSelectedRole, item.visibleKey) === '1';
html += `<div style="display:flex;align-items:center;gap:6px"><span style="font-size:11px;color:#94a3b8">Visa</span>${_permToggleHtml(v, `togglePermSetting('${item.visibleKey}',this.checked?'1':'0')`)}</div>`;
}
if(item.scopeKey){
const s = permGetSetting(permSelectedRole, item.scopeKey);
html += `<div style="display:flex;align-items:center;gap:6px"><span style="font-size:11px;color:#94a3b8">Data</span>${_permScopeSelect(s, `togglePermSetting('${item.scopeKey}',this.value)`)}</div>`;
}
if(item.toggleKey){
const t = permGetSetting(permSelectedRole, item.toggleKey) === '1';
html += `<div>${_permToggleHtml(t, `togglePermSetting('${item.toggleKey}',this.checked?'1':'0')`)}</div>`;
}
html += `</div>`;
});
html += `</div></div>`;
return html;
}
function togglePermPageExpand(pageKey){
permExpandedPages[pageKey] = !permExpandedPages[pageKey];
renderPermMatrix();
}
async function togglePermSetting(key, value){
if(!permSettings[permSelectedRole]) permSettings[permSelectedRole] = {};
permSettings[permSelectedRole][key] = String(value);
renderPermMatrix();
try {
await fetch('/api/permissions.php?action=settings', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ role: permSelectedRole, settings: { [key]: String(value) } })
});
if(permSelectedRole === gUserRole && typeof applyRoleSettings === 'function') applyRoleSettings();
} catch(e){ console.error('togglePermSetting error', e); }
}
async function togglePerm(pageKey, visible){
if(!permData[permSelectedRole]) permData[permSelectedRole] = {};
permData[permSelectedRole][pageKey] = visible ? 1 : 0;
renderPermMatrix();
try {
await fetch('/api/permissions.php', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({ role: permSelectedRole, permissions: { [pageKey]: visible ? 1 : 0 } })
});
} catch(e){ console.error('togglePerm error', e); }
if(permSelectedRole === gUserRole) applyNavPermissions(); if(typeof loadDashboard==="function") loadDashboard();
}
async function applyNavPermissions(){
try {
const res = await fetch('/api/permissions.php?role=' + encodeURIComponent(gUserRole || 'saljare') + '&t=' + Date.now());
const data = await res.json();
const perms = data.permissions || data;
const order = data.order || [];
document.querySelectorAll('.nav-item[data-page]').forEach(el => {
const page = el.dataset.page;
if(page === 'admin') return;
if(perms[page] !== undefined) {
el.style.display = perms[page] ? '' : 'none';
} else {
el.style.display = '';
}
});
if(order.length > 0) {
menuOrder = order;
applyNavOrder();
}
} catch(e){ console.error('applyNavPermissions error', e); }
}
const ROLE_LABELS = {systemadmin:'Systemadmin', admin:'Admin', saljchef:'Säljchef', saljare:'Säljare', installator:'Installatör', projektledare:'Projektledare', ekonomi:'Ekonomi', pending:'Väntande'};
const ROLE_OPTIONS = ['systemadmin','admin','saljchef','saljare','installator','projektledare','ekonomi'];
let allAdminUsers = [];
async function loadAdminUsers(){
try {
const res = await fetch('/api/staff.php?active=0&t='+Date.now());
const users = await res.json();
allAdminUsers = users.filter(u => u.approved == 1);
filterAdminUsers();
} catch(err){
console.error('loadAdminUsers error:', err);
}
}
function filterAdminUsers(){
const search = (document.getElementById('adminUserSearch')?.value || '').toLowerCase();
const roleFilter = document.getElementById('adminUserRoleFilter')?.value || '';
let filtered = allAdminUsers;
if(search) filtered = filtered.filter(u => (u.name||'').toLowerCase().includes(search) || (u.email||'').toLowerCase().includes(search));
if(roleFilter) filtered = filtered.filter(u => u.role === roleFilter);
const tbody = document.getElementById('adminUsersBody');
document.getElementById('adminUserCount').textContent = filtered.length + ' av ' + allAdminUsers.length + ' användare';
tbody.innerHTML = filtered.map(u => {
const roleOpts = ROLE_OPTIONS.filter(r => r !== 'systemadmin' || gUserRole === 'systemadmin').map(r => '<option value="'+r+'"'+(u.role===r?' selected':'')+'>'+ROLE_LABELS[r]+'</option>').join('');
const lastLogin = u.last_login ? new Date(u.last_login).toLocaleDateString('sv-SE') : '—';
return '<tr style="border-bottom:1px solid #f1f5f9">'
+'<td style="padding:10px 12px;font-weight:500">'+escHtml(u.name)+'</td>'
+'<td style="padding:10px 12px;color:#64748b">'+escHtml(u.email)+'</td>'
+'<td style="padding:10px 12px"><select onchange="changeUserRole('+u.id+',this.value)" style="padding:4px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;background:#fff;cursor:pointer">'+roleOpts+'</select></td>'
+'<td style="padding:10px 12px;color:#64748b;font-size:12px">'+(u.phone||'—')+'</td>'
+'<td style="padding:10px 12px;text-align:center"><button onclick="toggleUserActive('+u.id+','+(u.active?0:1)+')" style="width:36px;height:20px;border-radius:10px;border:none;cursor:pointer;background:'+(u.active==1?'#059669':'#cbd5e1')+';position:relative;transition:background .2s"><span style="position:absolute;top:2px;'+(u.active==1?'right:2px':'left:2px')+';width:16px;height:16px;background:#fff;border-radius:50%;transition:all .2s"></span></button></td>'
+'<td style="padding:10px 12px;color:#94a3b8;font-size:12px">'+lastLogin+'</td>'
+(gUserRole==='systemadmin'?'<td style="padding:10px 12px;text-align:center"><button onclick="deleteUser('+u.id+',\''+escHtml(u.name)+'\')" style="background:none;border:none;cursor:pointer;color:#ef4444;font-size:14px" title="Ta bort">×</button></td>':'<td></td>')
+'</tr>';
}).join('');
}
async function loadAdminPending(){
try {
const res = await fetch('/api/staff.php?approved=0&t='+Date.now());
const pending = await res.json();
const listEl = document.getElementById('adminPendingList');
const emptyEl = document.getElementById('adminPendingEmpty');
const countEl = document.getElementById('adminPendingCount');
const badgeEl = document.getElementById('adminPendingBadge');
if(pending.length === 0){
emptyEl.style.display='block';
listEl.style.display='none';
if(countEl) countEl.style.display='none';
if(badgeEl) badgeEl.style.display='none';
return;
}
emptyEl.style.display='none';
listEl.style.display='flex';
if(countEl){ countEl.textContent=pending.length; countEl.style.display='inline'; }
if(badgeEl){ badgeEl.textContent=pending.length; badgeEl.style.display='inline'; }
listEl.innerHTML = pending.map(u => {
const createdD = u.created_at ? new Date(u.created_at) : null;
const created = createdD ? createdD.toLocaleDateString('sv-SE')+' kl '+createdD.toLocaleTimeString('sv-SE',{hour:'2-digit',minute:'2-digit'}) : '—';
const roleOpts = ROLE_OPTIONS.filter(r => r !== 'systemadmin' || gUserRole === 'systemadmin').map(r => '<option value="'+r+'"'+(r==='saljare'?' selected':'')+'>'+ROLE_LABELS[r]+'</option>').join('');
return '<div style="background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap">'
+'<div style="flex:1;min-width:200px">'
+'<div style="font-weight:600;font-size:15px;color:#1e293b">'+escHtml(u.name)+'</div>'
+'<div style="font-size:13px;color:#64748b;margin-top:2px">'+escHtml(u.email)+'</div>'
+'<div style="font-size:11px;color:#94a3b8;margin-top:4px">Registrerad: '+created+'</div>'
+'</div>'
+'<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">'
+'<select id="pendingRole_'+u.id+'" style="padding:6px 10px;border:1px solid #e5e7eb;border-radius:8px;font-size:12px;font-family:inherit;background:#fff">'+roleOpts+'</select>'
+'<button onclick="approveUser('+u.id+')" style="padding:6px 16px;background:#059669;color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Godkänn</button>'
+(gUserRole==='systemadmin'?'<button onclick="rejectUser('+u.id+',\''+escHtml(u.name)+'\')" style="padding:6px 16px;background:#ef4444;color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit">Neka</button>':'')
+'</div></div>';
}).join('');
} catch(err){
console.error('loadAdminPending error:', err);
}
}
async function approveUser(id){
const roleSelect = document.getElementById('pendingRole_'+id);
const role = roleSelect ? roleSelect.value : 'saljare';
try {
await fetch('/api/staff.php?id='+id, {
method:'PUT', headers:{'Content-Type':'application/json'},
body: JSON.stringify({approved:1, role:role, active:1})
});
// Skicka välkomstmail
try {
const userRes = await fetch('/api/staff.php?id='+id);
const user = await userRes.json();
if(user && user.email){
await fetch('/api/mail.php', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({
to: user.email,
subject: 'Välkommen till SolarGroup!',
body: '<h2>Välkommen '+escHtml(user.name)+'!</h2><p>Ditt konto har godkänts. Du kan nu logga in på <a href="https://prodconfig.wenesthosting.com">Solar Sales Suite</a>.</p><p>Din roll: <strong>'+ROLE_LABELS[role]+'</strong></p><br><p>Med vänliga hälsningar,<br>SolarGroup Admin</p>'
})
});
}
} catch(mailErr){ console.warn('Welcome mail failed:', mailErr); }
loadAdminPending();
loadAdminUsers();
} catch(err){ alert('Fel: '+err.message); }
}
async function rejectUser(id, name){
if(gUserRole !== 'systemadmin'){ alert('Endast systemadmin kan neka användare.'); return; }
if(!confirm('Neka och ta bort '+name+'?')) return;
try {
await fetch('/api/staff.php?id='+id, { method:'DELETE' });
loadAdminPending();
} catch(err){ alert('Fel: '+err.message); }
}
async function changeUserRole(id, role){
try {
await fetch('/api/staff.php?id='+id, {
method:'PUT', headers:{'Content-Type':'application/json'},
body: JSON.stringify({role:role})
});
} catch(err){ alert('Fel: '+err.message); }
}
async function toggleUserActive(id, active){
try {
await fetch('/api/staff.php?id='+id, {
method:'PUT', headers:{'Content-Type':'application/json'},
body: JSON.stringify({active:active})
});
loadAdminUsers();
} catch(err){ alert('Fel: '+err.message); }
}
async function deleteUser(id, name){
if(gUserRole !== 'systemadmin'){ alert('Endast systemadmin kan ta bort användare.'); return; }
if(!confirm('Ta bort '+name+'? Detta kan inte ångras.')) return;
try {
await fetch('/api/staff.php?id='+id, { method:'DELETE' });
loadAdminUsers();
} catch(err){ alert('Fel: '+err.message); }
}
function escHtml(s){ return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
// Ladda admin-data när sidan visas
function initAdminPage(){
const searchEl = document.getElementById('adminUserSearch');
if(searchEl) searchEl.value = '';
const roleEl = document.getElementById('adminUserRoleFilter');
if(roleEl) roleEl.value = '';
loadAdminUsers();
loadAdminPending();
}
// Kolla väntande vid start (för badge)
async function checkPendingUsers(){
if(gUserRole !== 'systemadmin' && gUserRole !== 'admin' && gUserRole !== 'saljchef') return;
try {
const res = await fetch('/api/staff.php?approved=0&t='+Date.now());
const pending = await res.json();
const badge = document.getElementById('adminPendingBadge');
if(badge){
if(pending.length > 0){
badge.textContent = pending.length;
badge.style.display = 'inline';
} else {
badge.style.display = 'none';
}
}
} catch(e){}
}
/* === BILDGENERERING === */
let bgUploadedImage = null;
let bgGeneratedImages = JSON.parse(localStorage.getItem('bgGallery') || '[]');
function handleBgUpload(input) {
const file = input.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0);
bgUploadedImage = canvas.toDataURL('image/jpeg', 0.85);
document.getElementById('bgPreviewImg').src = bgUploadedImage;
document.getElementById('bgPreview').style.display = 'block';
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
function clearBgUpload() {
bgUploadedImage = null;
document.getElementById('bgPreview').style.display = 'none';
document.getElementById('bgFileInput').value = '';
document.getElementById('bgCameraInput').value = '';
}