/** * Settings page - form handlers for user preferences, filters, and weights. * Loaded on all pages via base.html so functions are available when * the settings section is dynamically loaded via HTMX. */ /** * Read a DaisyUI theme color from computed CSS custom properties. * Falls back to sensible defaults if the theme variable is not found. */ function getThemeColor(varName, fallback) { const style = getComputedStyle(document.documentElement); const value = style.getPropertyValue(varName).trim(); return value || fallback; } function saveSettings() { const data = { default_lang: document.getElementById('default_lang')?.value, theme: document.getElementById('theme')?.value, download_dir: document.getElementById('download_dir')?.value, }; const token = localStorage.getItem('auth_token'); if (!token) return; fetch('/api/settings', { method: 'PATCH', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => { if (r.ok) showToast('Preferences enregistrees', 'success'); }).catch(e => { showToast('Erreur: ' + e.message, 'error'); }); } function saveFilter(field, value) { const token = localStorage.getItem('auth_token'); if (!token) return; fetch('/api/settings', { method: 'PATCH', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ [field]: value }) }).then(r => { if (r.ok) showToast('Filtre mis a jour', 'success'); }).catch(e => { showToast('Erreur: ' + e.message, 'error'); }); } async function toggleCategory(field, value) { if (!value) { const otherField = field === 'anime_enabled' ? 'series_enabled' : 'anime_enabled'; const otherCheckbox = document.getElementById(otherField); if (otherCheckbox && !otherCheckbox.checked) { showToast('Au moins une categorie doit rester active', 'error'); document.getElementById(field).checked = true; return; } } const token = localStorage.getItem('auth_token'); if (!token) return; try { const r = await fetch('/api/settings', { method: 'PATCH', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ [field]: value }) }); if (!r.ok) { const err = await r.json().catch(() => ({})); showToast(err.detail || 'Erreur', 'error'); document.getElementById(field).checked = !value; } else { showToast('Categorie ' + (value ? 'activee' : 'desactivee'), 'success'); } } catch (e) { showToast('Erreur: ' + e.message, 'error'); document.getElementById(field).checked = !value; } } function onWeightModeChange(mode) { const autoInfo = document.getElementById('weight-auto-info'); const manualControls = document.getElementById('weight-manual-controls'); if (mode === 'auto') { if (autoInfo) autoInfo.style.display = 'block'; if (manualControls) manualControls.style.display = 'none'; loadAutoWeights(); } else { if (autoInfo) autoInfo.style.display = 'none'; if (manualControls) manualControls.style.display = 'block'; updateWeightPreview(); } const token = localStorage.getItem('auth_token'); if (!token) return; fetch('/api/settings', { method: 'PATCH', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ content_weight_mode: mode }) }); } async function loadAutoWeights() { const details = document.getElementById('weight-auto-details'); if (!details) return; const token = localStorage.getItem('auth_token'); if (!token) return; try { const r = await fetch('/api/settings/content-weight', { headers: { 'Authorization': 'Bearer ' + token } }); if (!r.ok) return; const data = await r.json(); const aw = data.anime_weight; const sw = data.series_weight; const ac = data.anime_count; const sc = data.series_count; const total = data.total || 0; const primary = getThemeColor('--color-primary', '#6366f1'); const secondary = getThemeColor('--color-secondary', '#a3a3a3'); const accent = getThemeColor('--color-accent', '#38bdf8'); const error = getThemeColor('--color-error', '#f43f5e'); const muted = getThemeColor('--color-base-content', '#999'); if (total === 0) { details.innerHTML = `Aucun telechargement detecte. Ratio par defaut : ${aw} anime / ${sw} serie.`; } else { const pctA = total > 0 ? Math.round(ac / total * 100) : 50; const pctS = total > 0 ? Math.round(sc / total * 100) : 50; details.innerHTML = `