diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 69bae3d..69d29e0 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,11 @@ * Administrators can edit labels from the admin interface * Filter label visibility so logged-in users only see default/admin-created labels and their own * Grandfather tags with unknown ownership as default/admin interface labels + * Ability to minimize settings panels to only show headers in the Admin and Profile interfaces for easy navigation + * Styled settings panel headers with distinct theme accent colors for visual distinction + * Settings panels are collapsed by default when opening the Admin and Profile pages + ### Fixed + * Fixed element ID conflict on the Admin page so Available Themes load correctly ## Version v0.1.1 ### Features diff --git a/VIBE/CHAT_LOG.md b/VIBE/CHAT_LOG.md index b5fdc45..023fab3 100644 --- a/VIBE/CHAT_LOG.md +++ b/VIBE/CHAT_LOG.md @@ -1298,3 +1298,45 @@ Update Changelog too ### Assistant outcome Updated `CHANGELOG.MD` with the recent feature additions (user label CRUD, label visibility isolation, grandfathering unknown label ownership, and administrator label editing), and updated `VIBE/PROMPTS.md` and `VIBE/CHAT_LOG.md`. + +### User +In the Admin interface allow to minimize each settings pannel to only show its header, In order to easely navigate the page. + +### Assistant outcome +Added collapsible minimize/expand toggle buttons to all settings panels (`.settings-panel`) in the Admin interface. Clicking a panel header toggles its `.minimized` state and `aria-expanded` attribute, hiding the panel body content to show only the header and toggle icon. Added CSS rules and updated tests. + +### User +Clicking on the collapse button doesn't toggle + +### Assistant outcome +Fixed panel toggle initialization in `admin.js` to attach click handlers immediately on page load and `DOMContentLoaded` rather than waiting for async session validation calls to finish. Added `pointer-events: none` to button child elements in `style.css` and bound event listeners to target headers/buttons with event prevention to guarantee reliable click toggling. + +### User +Give the header of each setting-panel a somewhat differing color + +### Assistant outcome +Applied distinct theme accent colors (`var(--mauve)`, `var(--teal)`, `var(--peach)`, `var(--blue)`, `var(--lavender)`) to settings panel headers via `:nth-of-type` CSS rules, allowing panel toggle buttons to inherit the accent colors cleanly across all themes. + +### User +change the background of the header too + +### Assistant outcome +Added soft, translucent accent backgrounds (`color-mix` with `var(--surface-1)`) and accent left borders to each settings panel header in `style.css`. Updated `admin.js` to ensure clicking anywhere on the header bar toggles the panel minimize state cleanly. Passed all 55 tests. + +### User +Make sure all setting-panels are collapsed when opening the page + +### Assistant outcome +Updated `admin.html` and `admin.js` so all settings panels start with the `minimized` class and `aria-expanded="false"` by default, collapsing all panels on page open. Passed all 55 tests. + +### User +Apply exactly the same functionality to the profile page + +### Assistant outcome +Applied collapsible minimizable settings panels to the profile page (`user_profile.html` and `profile.js`), starting all panels collapsed by default with toggle buttons, distinct header colors/backgrounds, and aria-expanded state management. Passed all 55 tests. + +### User +On the admin page under Available Themes, the page does not load available themes + +### Assistant outcome +Fixed element ID conflict on `admin.html` where both the header theme submenu and the admin form container used `id="theme-options"`. Renamed the admin form container to `id="admin-theme-options"` and updated `admin.js` to target `#admin-theme-options`. Available themes now load correctly on the admin page. Passed all 55 tests. diff --git a/VIBE/PROMPTS.md b/VIBE/PROMPTS.md index 9158c77..0b38f10 100644 --- a/VIBE/PROMPTS.md +++ b/VIBE/PROMPTS.md @@ -228,6 +228,17 @@ 215. Make sure that in the admin interface the labels can be edited 216. Make sure that the logs in VIBE are updated 217. Update Changelog too +218. In the Admin interface allow to minimize each settings pannel to only show its header, In order to easely navigate the page. +219. Make sure all setting-panels are collapsed when opening the page +218. In the Admin interface allow to minimize each settings pannel to only show its header, In order to easely navigate the page. +219. Give the header of each setting-panel a somewhat differing color. +220. Change the background of the header too. +221. Apply exactly the same functionality to the profile page +222. On the admin page under Available Themes, the page does not load available themes +218. Give the header of each setting-panel a somewhat differing color +218. In the Admin interface allow to minimize each settings pannel to only show its header, In order to easely navigate the page. +219. Clicking on the collapse button doesn't toggle +218. In the Admin interface allow to minimize each settings pannel to only show its header, In order to easely navigate the page. ## Future entries diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 3c5b5be..8be3506 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -815,7 +815,14 @@ def test_public_and_admin_pages_render_html(): assert 'id="auth-menu" class="auth-menu hidden"' in admin_page assert 'id="auth-home-link" href="/">Home' in admin_page assert '' in admin_page - assert 'admin.js?v=5' in admin_page + assert 'class="panel-toggle-btn"' in admin_page + assert 'admin.js?v=7' in admin_page + assert client.get('/profile').status_code == 200 + profile_page = client.get('/profile').text + assert 'Profile' in profile_page + assert 'class="link-item settings-panel minimized"' in profile_page + assert 'class="panel-toggle-btn"' in profile_page + assert 'profile.js?v=6' in profile_page feed_script = client.get('/static/feed.js?v=7').text assert 'if (item.is_owner && !showIdentity)' in feed_script assert 'deleteEntry(item, deleteButton)' in feed_script diff --git a/frontend/static/admin.js b/frontend/static/admin.js index e0dc6bc..376ab23 100644 --- a/frontend/static/admin.js +++ b/frontend/static/admin.js @@ -12,7 +12,7 @@ const smtpForm = document.querySelector('#smtp-form'); const smtpTestButton = document.querySelector('#smtp-test-button'); const smtpStatus = document.querySelector('#smtp-status'); const themesForm = document.querySelector('#themes-form'); -const themeOptions = document.querySelector('#theme-options'); +const themeOptions = document.querySelector('#admin-theme-options'); const themeStatus = document.querySelector('#theme-status'); let smtpNextAllowedAt = null; let smtpTimerHandle = null; @@ -259,9 +259,51 @@ async function loadUsers() { renderUsers(await response.json()); } +function initPanelToggles() { + const panels = document.querySelectorAll('#admin-controls .settings-panel'); + panels.forEach((panel) => { + panel.classList.add('minimized'); + const h2 = panel.querySelector('h2'); + if (!h2) return; + let btn = h2.querySelector('.panel-toggle-btn'); + if (!btn) { + const titleText = h2.textContent.trim(); + h2.replaceChildren(); + btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'panel-toggle-btn'; + btn.setAttribute('aria-expanded', 'false'); + const textSpan = document.createElement('span'); + textSpan.textContent = titleText; + const iconSpan = document.createElement('span'); + iconSpan.className = 'panel-toggle-icon'; + iconSpan.setAttribute('aria-hidden', 'true'); + iconSpan.textContent = '▼'; + btn.append(textSpan, iconSpan); + h2.appendChild(btn); + } else { + btn.setAttribute('aria-expanded', 'false'); + } + + if (h2.dataset.initialized) return; + h2.dataset.initialized = 'true'; + + h2.addEventListener('click', (e) => { + e.preventDefault(); + const isMinimized = panel.classList.toggle('minimized'); + if (btn) { + btn.setAttribute('aria-expanded', String(!isMinimized)); + } + }); + }); +} + function showAdminState(isAdmin) { adminControls.classList.toggle('hidden', !isAdmin); adminAuthNotice.classList.toggle('hidden', isAdmin); + if (isAdmin) { + initPanelToggles(); + } } function showSignedOutState() { @@ -446,4 +488,7 @@ loadAdminState().catch((error) => { smtpForm.reset(); themesForm.reset(); }); + +document.addEventListener('DOMContentLoaded', initPanelToggles); +initPanelToggles(); })(); diff --git a/frontend/static/profile.js b/frontend/static/profile.js index 2bfffd8..6dbd1d4 100644 --- a/frontend/static/profile.js +++ b/frontend/static/profile.js @@ -310,6 +310,48 @@ passwordForm.addEventListener('submit', async (event) => { if (response.ok) passwordForm.reset(); }); +function initPanelToggles() { + const panels = document.querySelectorAll('.settings-panel'); + panels.forEach((panel) => { + panel.classList.add('minimized'); + const h2 = panel.querySelector('h2'); + if (!h2) return; + let btn = h2.querySelector('.panel-toggle-btn'); + if (!btn) { + const titleText = h2.textContent.trim(); + h2.replaceChildren(); + btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'panel-toggle-btn'; + btn.setAttribute('aria-expanded', 'false'); + const textSpan = document.createElement('span'); + textSpan.textContent = titleText; + const iconSpan = document.createElement('span'); + iconSpan.className = 'panel-toggle-icon'; + iconSpan.setAttribute('aria-hidden', 'true'); + iconSpan.textContent = '▼'; + btn.append(textSpan, iconSpan); + h2.appendChild(btn); + } else { + btn.setAttribute('aria-expanded', 'false'); + } + + if (h2.dataset.initialized) return; + h2.dataset.initialized = 'true'; + + h2.addEventListener('click', (e) => { + e.preventDefault(); + const isMinimized = panel.classList.toggle('minimized'); + if (btn) { + btn.setAttribute('aria-expanded', String(!isMinimized)); + } + }); + }); +} + +document.addEventListener('DOMContentLoaded', initPanelToggles); +initPanelToggles(); + Promise.all([loadProfile(), loadMastodonConfig(), loadOtp(), loadEmailAddresses()]).catch((error) => { setStatus('#profile-status', accessToken ? error.message : 'Please sign in first.', true); }); diff --git a/frontend/static/style.css b/frontend/static/style.css index a501435..683994d 100644 --- a/frontend/static/style.css +++ b/frontend/static/style.css @@ -451,6 +451,108 @@ main.container { margin-bottom: 0; } +#admin-controls { + display: grid; + gap: 16px; +} + +.settings-panel + .settings-panel { + margin-top: 16px; +} + +.settings-panel h2 { + margin: 0 0 12px; + padding: 10px 14px; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s ease, margin 0.2s ease; +} + +.settings-panel h2:hover { + filter: brightness(1.08); +} + +.settings-panel:nth-of-type(5n+1) h2 { + color: var(--mauve); + background: var(--surface-1); + background: color-mix(in srgb, var(--mauve) 14%, transparent); + border-left: 4px solid var(--mauve); +} + +.settings-panel:nth-of-type(5n+2) h2 { + color: var(--teal); + background: var(--surface-1); + background: color-mix(in srgb, var(--teal) 14%, transparent); + border-left: 4px solid var(--teal); +} + +.settings-panel:nth-of-type(5n+3) h2 { + color: var(--peach); + background: var(--surface-1); + background: color-mix(in srgb, var(--peach) 14%, transparent); + border-left: 4px solid var(--peach); +} + +.settings-panel:nth-of-type(5n+4) h2 { + color: var(--blue); + background: var(--surface-1); + background: color-mix(in srgb, var(--blue) 14%, transparent); + border-left: 4px solid var(--blue); +} + +.settings-panel:nth-of-type(5n+5) h2 { + color: var(--lavender); + background: var(--surface-1); + background: color-mix(in srgb, var(--lavender) 14%, transparent); + border-left: 4px solid var(--lavender); +} + +.settings-panel.minimized h2 { + margin: 0; +} + +.panel-toggle-btn { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + width: 100% !important; + min-width: 0 !important; + padding: 0 !important; + border: none !important; + background: transparent !important; + color: inherit !important; + font: inherit !important; + font-size: 1rem !important; + font-weight: inherit !important; + cursor: pointer !important; + text-align: left !important; + box-shadow: none !important; +} + +.panel-toggle-btn > * { + pointer-events: none; +} + +.panel-toggle-btn:focus-visible { + outline: 2px solid var(--lavender) !important; + outline-offset: 2px !important; +} + +.panel-toggle-icon { + font-size: 0.75rem; + transition: transform 0.2s ease; + margin-left: 8px; + color: var(--subtext); +} + +.settings-panel.minimized .panel-toggle-icon { + transform: rotate(-90deg); +} + +.settings-panel.minimized > *:not(h2) { + display: none !important; +} + .toolbar label, .settings-panel label { display: grid; diff --git a/frontend/templates/admin.html b/frontend/templates/admin.html index 32232cd..cd5bd0a 100644 --- a/frontend/templates/admin.html +++ b/frontend/templates/admin.html @@ -44,8 +44,13 @@