Fix Learning Hub search and content viewer isolation
Search (was completely broken):
- wireSearch() registered inside tabChanged for 'learning' tab — runs
after component HTML is in the DOM (lh-search existed but listener
was never attached because element was null at IIFE load time)
- Proper debounced search with 350ms delay, Escape key to clear
- Search results show count, 'X results for "query"', Clear Search button
- Empty state with helpful message; loading state while fetching
Content viewer showing feed items beneath it (CSS cascade bug):
- .lh-feed{display:flex} and .lh-category-bar{display:flex} defined
at CSS lines 513/507 — after .hidden{display:none} at line 242
So classList.add('hidden') was silently overridden
- showFeed() and showViewer() now use element.style.display with
explicit values ('flex'/'block'/'none') which always win over class rules
- viewer started hidden via class="hidden"; now shown with style.display='block'
This commit is contained in:
parent
ac8cc38e3b
commit
747fc3322a
1 changed files with 66 additions and 26 deletions
|
|
@ -13,7 +13,7 @@
|
|||
// ── Load when tab activated ────────────────────────────────
|
||||
document.addEventListener('tabChanged', function(e) {
|
||||
if (e.detail && e.detail.tab === 'learning') {
|
||||
if (!loaded) { loadFeed(); loadCategories(); loaded = true; }
|
||||
if (!loaded) { loadFeed(); loadCategories(); loaded = true; wireSearch(); }
|
||||
}
|
||||
// Load CMS when Content Manager tab is opened
|
||||
if (e.detail && e.detail.tab === 'cms') {
|
||||
|
|
@ -134,15 +134,20 @@
|
|||
if (rmOpt) { var row = rmOpt.closest('.lh-option-row'); if (row) row.remove(); return; }
|
||||
});
|
||||
|
||||
// Search
|
||||
var searchTimeout = null;
|
||||
var searchEl = document.getElementById('lh-search');
|
||||
if (searchEl) {
|
||||
// wireSearch() called after component is in DOM
|
||||
var _searchTimeout = null;
|
||||
function wireSearch() {
|
||||
var searchEl = document.getElementById('lh-search');
|
||||
if (!searchEl || searchEl._wired) return;
|
||||
searchEl._wired = true;
|
||||
searchEl.addEventListener('input', function() {
|
||||
clearTimeout(searchTimeout);
|
||||
clearTimeout(_searchTimeout);
|
||||
var q = searchEl.value.trim();
|
||||
if (!q) { loadFeed(); return; }
|
||||
searchTimeout = setTimeout(function() { searchContent(q); }, 300);
|
||||
_searchTimeout = setTimeout(function() { searchContent(q); }, 350);
|
||||
});
|
||||
searchEl.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') { searchEl.value = ''; loadFeed(); }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -198,13 +203,46 @@
|
|||
var feedEl = document.getElementById('lh-feed');
|
||||
if (!feedEl) return;
|
||||
showFeed();
|
||||
feedEl.innerHTML =
|
||||
'<div style="padding:8px 0 12px;font-size:13px;color:var(--g500);">' +
|
||||
'<i class="fas fa-search"></i> Searching for <strong>"' + esc(q) + '"</strong>…' +
|
||||
'</div>';
|
||||
|
||||
fetch('/api/learning/search?q=' + encodeURIComponent(q), { headers: getAuthHeaders() })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.success) return;
|
||||
renderFeed(data.content, feedEl);
|
||||
});
|
||||
if (!data.success) { feedEl.innerHTML = '<p style="color:var(--red);padding:12px;">Search failed</p>'; return; }
|
||||
var header = '<div style="padding:8px 0 12px;display:flex;align-items:center;justify-content:space-between;">' +
|
||||
'<span style="font-size:13px;color:var(--g600);">' +
|
||||
(data.content.length > 0
|
||||
? '<strong>' + data.content.length + '</strong> result' + (data.content.length !== 1 ? 's' : '') + ' for <strong>"' + esc(q) + '"</strong>'
|
||||
: 'No results for <strong>"' + esc(q) + '"</strong>') +
|
||||
'</span>' +
|
||||
'<button id="lh-search-clear" style="background:none;border:none;font-size:12px;color:var(--blue);cursor:pointer;padding:0;">Clear search</button>' +
|
||||
'</div>';
|
||||
|
||||
if (data.content.length === 0) {
|
||||
feedEl.innerHTML = header +
|
||||
'<div style="text-align:center;padding:40px;color:var(--g400);">' +
|
||||
'<i class="fas fa-search" style="font-size:32px;margin-bottom:12px;display:block;opacity:0.4;"></i>' +
|
||||
'Try different keywords or browse by category' +
|
||||
'</div>';
|
||||
} else {
|
||||
var tempEl = document.createElement('div');
|
||||
renderFeed(data.content, tempEl);
|
||||
feedEl.innerHTML = header + tempEl.innerHTML;
|
||||
}
|
||||
|
||||
var clearBtn = document.getElementById('lh-search-clear');
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', function() {
|
||||
var s = document.getElementById('lh-search');
|
||||
if (s) s.value = '';
|
||||
loadFeed();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function() { feedEl.innerHTML = '<p style="color:var(--red);padding:12px;">Search error</p>'; });
|
||||
}
|
||||
|
||||
function renderFeed(items, feedEl) {
|
||||
|
|
@ -250,26 +288,28 @@
|
|||
|
||||
function showFeed() {
|
||||
currentView = 'feed';
|
||||
var feedEl = document.getElementById('lh-feed');
|
||||
var viewerEl = document.getElementById('lh-viewer');
|
||||
var catsEl = document.getElementById('lh-categories');
|
||||
var searchWrap = document.getElementById('lh-search');
|
||||
if (feedEl) feedEl.classList.remove('hidden');
|
||||
if (viewerEl) viewerEl.classList.add('hidden');
|
||||
if (catsEl) catsEl.classList.remove('hidden');
|
||||
if (searchWrap) searchWrap.closest('.card').classList.remove('hidden');
|
||||
var feedEl = document.getElementById('lh-feed');
|
||||
var viewerEl = document.getElementById('lh-viewer');
|
||||
var catsEl = document.getElementById('lh-categories');
|
||||
var searchEl = document.getElementById('lh-search');
|
||||
// Use style.display — classList.add('hidden') is overridden by .lh-feed{display:flex}
|
||||
// and .lh-category-bar{display:flex} which are defined later in CSS
|
||||
if (feedEl) feedEl.style.display = 'flex';
|
||||
if (viewerEl) viewerEl.style.display = 'none';
|
||||
if (catsEl) catsEl.style.display = 'flex';
|
||||
if (searchEl) { var sc = searchEl.closest('.card'); if (sc) sc.style.display = ''; }
|
||||
}
|
||||
|
||||
function showViewer(item) {
|
||||
currentView = 'viewer';
|
||||
var feedEl = document.getElementById('lh-feed');
|
||||
var viewerEl = document.getElementById('lh-viewer');
|
||||
var catsEl = document.getElementById('lh-categories');
|
||||
var searchWrap = document.getElementById('lh-search');
|
||||
if (feedEl) feedEl.classList.add('hidden');
|
||||
if (viewerEl) viewerEl.classList.remove('hidden');
|
||||
if (catsEl) catsEl.classList.add('hidden');
|
||||
if (searchWrap) searchWrap.closest('.card').classList.add('hidden');
|
||||
var feedEl = document.getElementById('lh-feed');
|
||||
var viewerEl = document.getElementById('lh-viewer');
|
||||
var catsEl = document.getElementById('lh-categories');
|
||||
var searchEl = document.getElementById('lh-search');
|
||||
if (feedEl) feedEl.style.display = 'none';
|
||||
if (viewerEl) viewerEl.style.display = 'block';
|
||||
if (catsEl) catsEl.style.display = 'none';
|
||||
if (searchEl) { var sc = searchEl.closest('.card'); if (sc) sc.style.display = 'none'; }
|
||||
|
||||
var titleEl = document.getElementById('lh-viewer-title');
|
||||
var metaEl = document.getElementById('lh-viewer-meta');
|
||||
|
|
|
|||
Loading…
Reference in a new issue