const API = ''; let currentPage = 'dashboard'; async function api(path, opts = {}) { const res = await fetch(API + path, { headers: { 'Content-Type': 'application/json' }, ...opts }); return res.json(); } function toast(msg) { let t = document.querySelector('.toast'); if (!t) { t = document.createElement('div'); t.className = 'toast'; document.body.appendChild(t); } t.textContent = msg; t.classList.add('show'); setTimeout(() => t.classList.remove('show'), 3000); } function navigate(page) { currentPage = page; document.querySelectorAll('#nav-links a').forEach(a => { a.classList.toggle('active', a.dataset.page === page); }); renderPage(page); } async function renderPage(page) { const el = document.getElementById('content'); switch (page) { case 'dashboard': return renderDashboard(el); case 'albums': return renderAlbums(el); case 'slideshow': return renderSlideshow(el); case 'display': return renderDisplay(el); case 'device': return renderDevice(el); case 'firmware': return renderFirmware(el); } } async function renderDashboard(el) { const status = await api('/api/status'); el.innerHTML = `

Status

Battery${status.battery}%${status.charging ? ' ⚡' : ''}
WiFi Signal${status.wifi_rssi} dBm
IP Address${status.ip}
Uptime${Math.floor(status.uptime/60)}m
Free RAM${Math.floor(status.free_heap/1024)}KB
Free PSRAM${Math.floor(status.free_psram/1024)}KB

Controls

`; } async function renderAlbums(el) { const [albums, settings] = await Promise.all([api('/api/albums'), api('/api/settings')]); const selected = JSON.parse(settings.albums_json || '[]'); el.innerHTML = `

Albums

`; document.getElementById('saveAlbums').onclick = async () => { const ids = [...el.querySelectorAll('input[type=checkbox]:checked')].map(c => c.value); await api('/api/albums/select', { method: 'POST', body: JSON.stringify({ album_ids: ids }) }); toast('Albums saved'); }; } async function renderSlideshow(el) { const settings = await api('/api/settings'); const intervals = [1, 5, 15, 30, 60]; const modes = ['Random', 'Chronological', 'Reverse Chrono', 'Favorites Weighted']; el.innerHTML = `

Interval

${intervals.map(i => ``).join('')}

Cycling Mode

`; el.querySelectorAll('[data-interval]').forEach(btn => { btn.onclick = () => { el.querySelectorAll('[data-interval]').forEach(b => b.classList.remove('active')); btn.classList.add('active'); }; }); document.getElementById('saveSlideshow').onclick = async () => { const interval = parseInt(el.querySelector('[data-interval].active')?.dataset.interval || '5'); const mode = parseInt(document.getElementById('cycleMode').value); await api('/api/settings', { method: 'POST', body: JSON.stringify({ interval_min: interval, cycle_mode: mode }) }); toast('Slideshow settings saved'); }; } async function renderDisplay(el) { const settings = await api('/api/settings'); const flags = settings.meta_flags; el.innerHTML = `

Image Quality

Metadata Overlay

`; document.getElementById('saveDisplay').onclick = async () => { let flags = 0; el.querySelectorAll('[data-flag]').forEach(cb => { if (cb.checked) flags |= parseInt(cb.dataset.flag); }); await api('/api/settings', { method: 'POST', body: JSON.stringify({ img_quality: parseInt(document.getElementById('imgQuality').value), meta_flags: flags, meta_pos: parseInt(document.getElementById('metaPos').value) })}); toast('Display settings saved'); }; } async function renderDevice(el) { const settings = await api('/api/settings'); el.innerHTML = `

Immich Connection

Device Actions

`; document.getElementById('saveImmich').onclick = async () => { const url = document.getElementById('immichUrl').value.trim(); const key = document.getElementById('immichKey').value.trim(); if (!url || !key) return toast('URL and API key are required'); await api('/api/settings', { method: 'POST', body: JSON.stringify({ immich_url: url, immich_key: key }) }); toast('Immich settings saved. Reboot to apply.'); }; document.getElementById('testConn').onclick = async () => { const status = document.getElementById('connStatus'); status.textContent = 'Testing...'; status.style.color = ''; try { const result = await api('/api/test-connection', { method: 'POST' }); status.textContent = result.msg; status.style.color = result.ok ? '#2e7d32' : '#c62828'; } catch (e) { status.textContent = 'Request failed: ' + e.message; status.style.color = '#c62828'; } }; } async function renderFirmware(el) { el.innerHTML = `

Firmware Update

`; document.getElementById('uploadFw').onclick = async () => { const file = document.getElementById('fwFile').files[0]; if (!file) return toast('Select a file first'); const formData = new FormData(); formData.append('firmware', file); document.getElementById('fwStatus').textContent = 'Uploading...'; const res = await fetch('/api/firmware', { method: 'POST', body: formData }); const data = await res.json(); document.getElementById('fwStatus').textContent = data.msg || (data.ok ? 'Success!' : 'Failed'); }; } // Navigation document.querySelectorAll('#nav-links a').forEach(a => { a.addEventListener('click', (e) => { e.preventDefault(); navigate(a.dataset.page); }); }); // Initial render navigate('dashboard');