screentinker/frontend/js/branding.js
ScreenTinker 281a735e84 Fix white-label settings not applying on page load
Root cause: the Settings page loaded /api/white-label into the form
inputs but never applied the saved values (primary_color, bg_color,
brand_name, favicon, custom_css) to the actual document. Nothing in
app.js bootstrap touched branding. So the save hit the DB correctly,
reload kept the DB value correctly, but the page always rendered the
hardcoded defaults from css/variables.css and the static "ScreenTinker"
label in index.html — which looked like the save had reverted.

Fix: new frontend/js/branding.js module that fetches /api/white-label
once at startup (app.js) and applies values to:
  - --accent and --bg-primary CSS vars
  - document.title and the .sidebar-header .logo span text
  - all <link rel="icon">/apple-touch-icon hrefs
  - a <style id="wl-custom-css"> tag for custom_css
  - the theme-color meta tag

Settings save now calls resetBranding() after POST so changes apply
immediately without a reload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 19:36:20 -05:00

60 lines
1.8 KiB
JavaScript

// Applies the current user's saved white-label config to the DOM.
// Runs once after login/route bootstrap. Without this, saved values in the
// white_labels table are read into the Settings form but never applied to
// the actual page — so users see "ScreenTinker" and default colors after
// every reload, as if their save reverted.
let applied = false;
export async function applyBranding() {
if (applied) return;
applied = true;
const token = localStorage.getItem('token');
if (!token) return;
let wl;
try {
const res = await fetch('/api/white-label', { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return;
wl = await res.json();
} catch { return; }
if (!wl) return;
const root = document.documentElement;
if (wl.primary_color) root.style.setProperty('--accent', wl.primary_color);
if (wl.bg_color) {
root.style.setProperty('--bg-primary', wl.bg_color);
const meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute('content', wl.bg_color);
}
if (wl.brand_name) {
document.title = wl.brand_name;
const span = document.querySelector('.sidebar-header .logo span');
if (span) span.textContent = wl.brand_name;
}
if (wl.favicon_url) {
document.querySelectorAll('link[rel="icon"], link[rel="apple-touch-icon"]').forEach(l => {
l.setAttribute('href', wl.favicon_url);
});
}
if (wl.custom_css) {
let style = document.getElementById('wl-custom-css');
if (!style) {
style = document.createElement('style');
style.id = 'wl-custom-css';
document.head.appendChild(style);
}
style.textContent = wl.custom_css;
}
}
// Force a re-apply (called from settings.js after save)
export function resetBranding() {
applied = false;
return applyBranding();
}