From 006c865b87e98e5220f8bc02b3b3cf21c0247d01 Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:00:42 -0400 Subject: [PATCH] Add emoji toggle setting to parent dashboard Settings panel Parent can turn the floating school emoji on/off from Settings > Student home screen. Setting persists in the DB and is read via a public /api/public-settings endpoint on the student home page. Co-Authored-By: Claude Sonnet 4.6 --- public/admin.html | 9 +++++++++ public/admin.js | 4 +++- public/app.js | 4 ++++ public/styles.css | 19 +++++++++++++++++++ server.js | 8 +++++++- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/public/admin.html b/public/admin.html index a0ef43b..8605b9e 100644 --- a/public/admin.html +++ b/public/admin.html @@ -535,6 +535,15 @@ +
+ + +
+ diff --git a/public/admin.js b/public/admin.js index 5e3dbc1..f8b4d26 100644 --- a/public/admin.js +++ b/public/admin.js @@ -2488,6 +2488,7 @@ async function loadSettings() { $('#setting-passing-pct').value = appSettings.passingPct; const radio = document.querySelector(`input[name="week_start_day"][value="${appSettings.weekStartDay}"]`); if (radio) radio.checked = true; + $('#setting-show-emoji').checked = s.show_home_emoji !== false; } $('#app-settings-form').addEventListener('submit', async (e) => { @@ -2495,7 +2496,8 @@ $('#app-settings-form').addEventListener('submit', async (e) => { const school_name = $('#setting-school-name').value.trim(); const passing_pct = Number($('#setting-passing-pct').value); const week_start_day = document.querySelector('input[name="week_start_day"]:checked')?.value || 'monday'; - await api('/api/admin/app-settings', { method: 'POST', body: { school_name, passing_pct, week_start_day } }); + const show_home_emoji = $('#setting-show-emoji').checked; + await api('/api/admin/app-settings', { method: 'POST', body: { school_name, passing_pct, week_start_day, show_home_emoji } }); appSettings.schoolName = school_name; appSettings.passingPct = passing_pct; if (appSettings.weekStartDay !== week_start_day) { diff --git a/public/app.js b/public/app.js index 13006e6..3c0f33a 100644 --- a/public/app.js +++ b/public/app.js @@ -243,6 +243,10 @@ async function loadHome() { : 'Good evening! 🌙'; document.querySelector('.subtitle').textContent = greeting; + const pubSettings = await fetch('/api/public-settings').then((r) => r.json()).catch(() => ({})); + document.querySelector('.page-confetti').style.display = + pubSettings.show_home_emoji === false ? 'none' : ''; + const students = await api('/api/students'); $('#no-kids').hidden = students.length > 0; $('#kid-cards').innerHTML = students diff --git a/public/styles.css b/public/styles.css index a55fc5b..83da4c8 100644 --- a/public/styles.css +++ b/public/styles.css @@ -843,6 +843,25 @@ button.tiny { font-size: 0.75rem; padding: 0.15rem 0.5rem; } .radio-label { display: block; margin: 0.3rem 0; cursor: pointer; } .radio-label input { margin-right: 0.4rem; } +.toggle-label { + display: flex; align-items: center; gap: 0.65rem; + cursor: pointer; font-size: 0.95rem; user-select: none; +} +.toggle-label input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; } +.toggle-track { + flex-shrink: 0; width: 40px; height: 22px; + background: #d0ccc0; border-radius: 999px; + position: relative; transition: background 0.2s; +} +.toggle-thumb { + position: absolute; top: 3px; left: 3px; + width: 16px; height: 16px; border-radius: 50%; + background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.2); + transition: transform 0.2s; +} +.toggle-label input:checked ~ .toggle-track { background: var(--accent); } +.toggle-label input:checked ~ .toggle-track .toggle-thumb { transform: translateX(18px); } + /* ---------- new features: locked items, due dates, overdue, history, modal ---------- */ .today-row.locked { opacity: 0.55; diff --git a/server.js b/server.js index c279084..934ffdd 100644 --- a/server.js +++ b/server.js @@ -673,18 +673,24 @@ app.get('/api/admin/app-settings', requirePin, (req, res) => { school_name: getSetting('school_name') || '', passing_pct: Number(getSetting('passing_pct') || 80), week_start_day: getSetting('week_start_day') || 'monday', + show_home_emoji: getSetting('show_home_emoji') !== 'false', }); }); app.post('/api/admin/app-settings', requirePin, (req, res) => { - const { school_name, passing_pct, week_start_day } = req.body; + const { school_name, passing_pct, week_start_day, show_home_emoji } = req.body; const upsert = (k, v) => db.prepare(`INSERT INTO settings (key,value) VALUES (?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`).run(k, String(v)); if (school_name !== undefined) upsert('school_name', String(school_name).trim()); if (passing_pct !== undefined) upsert('passing_pct', Math.max(0, Math.min(100, Number(passing_pct)))); if (week_start_day !== undefined && ['monday', 'sunday'].includes(week_start_day)) upsert('week_start_day', week_start_day); + if (show_home_emoji !== undefined) upsert('show_home_emoji', show_home_emoji ? 'true' : 'false'); res.json({ ok: true }); }); +app.get('/api/public-settings', (req, res) => { + res.json({ show_home_emoji: getSetting('show_home_emoji') !== 'false' }); +}); + app.post('/api/students', requirePin, (req, res) => { const name = String(req.body.name || '').trim(); if (!name) return res.status(400).json({ error: 'Name required' });