Update web/templates/search.html
This commit is contained in:
parent
ce389d11bc
commit
ddc3194250
@ -10,7 +10,7 @@
|
||||
<input type="text" name="q" value="{{ q }}" placeholder="Type to search…" class="search-input" autofocus>
|
||||
<button class="btn btn-primary">Search</button>
|
||||
|
||||
<!-- Help button stays inline with Search -->
|
||||
<!-- Help button -->
|
||||
<button
|
||||
class="btn btn-secondary help-toggle"
|
||||
type="button"
|
||||
@ -20,7 +20,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Help panel sits below the row -->
|
||||
<!-- Help panel -->
|
||||
<div id="search-help-panel" class="help-panel">
|
||||
<h3>How to Use Search Operators</h3>
|
||||
<ul>
|
||||
@ -28,17 +28,15 @@
|
||||
<em>Example:</em> <code>faith</code></li>
|
||||
<li><strong>Phrase search</strong> — put quotes around a phrase to match it exactly.<br>
|
||||
<em>Example:</em> <code>"Jehovah is my shepherd"</code></li>
|
||||
<li><strong>OR search</strong> — use <code>OR</code> (uppercase) to match any of several terms.<br>
|
||||
<li><strong>OR search</strong> — use <code>OR</code> (uppercase).<br>
|
||||
<em>Example:</em> <code>love OR kindness</code></li>
|
||||
<li><strong>Exclude terms</strong> — use a minus sign to remove results containing a word.<br>
|
||||
<li><strong>Exclude terms</strong> — use <code>-</code> to remove.<br>
|
||||
<em>Example:</em> <code>hope -future</code></li>
|
||||
<li><strong>Wildcard search</strong> — use <code>*</code> to replace part of a word.<br>
|
||||
<em>Example:</em> <code>lov*</code> finds <code>love</code>, <code>loved</code>, <code>loving</code></li>
|
||||
<li><strong>Scripture search</strong> — type a Bible book or abbreviation to find entries that reference it.<br>
|
||||
<li><strong>Wildcard search</strong> — use <code>*</code>.<br>
|
||||
<em>Example:</em> <code>lov*</code></li>
|
||||
<li><strong>Scripture search</strong> — type a Bible book.<br>
|
||||
<em>Example:</em> <code>John 3:16</code></li>
|
||||
</ul>
|
||||
<p><strong>Tip:</strong> Combine operators:<br>
|
||||
<em>Example:</em> <code>"good shepherd" OR "faithful servant" -parable</code></p>
|
||||
</div>
|
||||
|
||||
<div class="filter-row">
|
||||
@ -58,29 +56,26 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- ===== Collapsible History card (kept same IDs) ===== -->
|
||||
<!-- ===== History Dropdown ===== -->
|
||||
<div class="card" style="padding:0; margin:16px 0 12px;">
|
||||
<button id="historyToggle"
|
||||
class="collapse-bar"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls="historyBody">
|
||||
<h1 class="page-title">History</h1>
|
||||
<span class="chev" aria-hidden="true">▸</span>
|
||||
</button>
|
||||
<h1 class="page-title dropdown-toggle" data-target="#history-panel">
|
||||
Search History
|
||||
<span class="chevron">▾</span>
|
||||
</h1>
|
||||
<div id="history-panel" class="dropdown-panel">
|
||||
<hr style="border:none; border-top:1px solid var(--border); margin:12px 0;">
|
||||
|
||||
<div id="historyBody" class="collapse-body" hidden>
|
||||
<!-- Recent Searches -->
|
||||
<div style="padding:12px 12px 0;">
|
||||
<div style="margin-top:6px;">
|
||||
<div class="small muted" style="margin-bottom:6px;">Your Recent Searches</div>
|
||||
<ul id="searchHistoryList" class="small" style="margin:0; padding-left:18px;"></ul>
|
||||
<div id="searchHistoryEmpty" class="muted small" style="display:none;">No history yet.</div>
|
||||
</div>
|
||||
|
||||
<hr style="border:none; border-top:1px solid var(--border); margin:12px;">
|
||||
<hr style="border:none; border-top:1px solid var(--border); margin:12px 0;">
|
||||
|
||||
<!-- Recently Viewed -->
|
||||
<div style="padding:0 12px 12px;">
|
||||
<div>
|
||||
<div class="small muted" style="margin-bottom:6px;">Recently Viewed Illustrations</div>
|
||||
<ul id="recentViewsList" class="small" style="margin:0; padding-left:18px;"></ul>
|
||||
<div id="recentViewsEmpty" class="muted small" style="display:none;">Nothing yet—open an illustration and linger 10s.</div>
|
||||
@ -91,20 +86,20 @@
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
// --- CSRF helper for fetch (not used by beacon) ---
|
||||
// --- CSRF helper ---
|
||||
function getCookie(name){
|
||||
const m = document.cookie.match('(^|;)\\s*'+name+'\\s*=\\s*([^;]+)');
|
||||
return m ? m.pop() : '';
|
||||
}
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
|
||||
// Helper: build entry view URL from a known-good template
|
||||
// Build entry view URL
|
||||
const ENTRY_URL_TEMPLATE = "{% url 'entry_view' 1 %}";
|
||||
function entryUrlFor(id){
|
||||
return ENTRY_URL_TEMPLATE.replace(/\/\d+\/?$/, '/' + id + '/');
|
||||
}
|
||||
|
||||
// Helper: first N words with ellipsis
|
||||
// First N words
|
||||
function firstWords(text, n){
|
||||
const clean = (text || '').replace(/\s+/g, ' ').trim();
|
||||
if (!clean) return '';
|
||||
@ -113,72 +108,47 @@
|
||||
return words.slice(0, n).join(' ') + '…';
|
||||
}
|
||||
|
||||
// --- Font prefs: fetch + apply + persist (unchanged) ---
|
||||
// Font prefs
|
||||
function applyFont(size){
|
||||
const root = document.documentElement;
|
||||
root.classList.remove('fs-small','fs-default','fs-large','fs-xlarge');
|
||||
root.classList.add('fs-'+size);
|
||||
document.querySelectorAll('[data-font-choice]').forEach(b=>{
|
||||
b.classList.toggle('primary', b.getAttribute('data-font-choice')===size);
|
||||
});
|
||||
}
|
||||
fetch("{% url 'api_get_prefs' %}").then(r=>r.json()).then(j=>{
|
||||
if (j.ok) applyFont(j.font_size || 'default');
|
||||
}).catch(()=>{});
|
||||
|
||||
document.querySelectorAll('[data-font-choice]').forEach(btn=>{
|
||||
btn.addEventListener('click', ()=>{
|
||||
const size = btn.getAttribute('data-font-choice');
|
||||
applyFont(size);
|
||||
const fd = new FormData(); fd.append('size', size);
|
||||
fetch("{% url 'api_set_font_size' %}", {
|
||||
method:'POST', body:fd, headers:{'X-CSRFToken': csrftoken}
|
||||
}).catch(()=>{});
|
||||
});
|
||||
});
|
||||
|
||||
// --- Collapsible History toggle ---
|
||||
const toggle = document.getElementById('historyToggle');
|
||||
const body = document.getElementById('historyBody');
|
||||
if (toggle && body){
|
||||
toggle.addEventListener('click', ()=>{
|
||||
const isOpen = !body.hidden;
|
||||
body.hidden = isOpen;
|
||||
toggle.setAttribute('aria-expanded', String(!isOpen));
|
||||
const chev = toggle.querySelector('.chev');
|
||||
if (chev) chev.textContent = isOpen ? '▸' : '▾';
|
||||
});
|
||||
}
|
||||
|
||||
// --- Search history: fetch + render ---
|
||||
const histList = document.getElementById('searchHistoryList');
|
||||
const histEmpty = document.getElementById('searchHistoryEmpty');
|
||||
// Search history (PUT BACK selected-fields subtitle)
|
||||
function renderHistory(items){
|
||||
histList.innerHTML = '';
|
||||
if (!items || !items.length){ histEmpty.style.display='block'; return; }
|
||||
histEmpty.style.display = 'none';
|
||||
const list = document.getElementById('searchHistoryList');
|
||||
const empty = document.getElementById('searchHistoryEmpty');
|
||||
list.innerHTML = '';
|
||||
if (!items || !items.length){ empty.style.display='block'; return; }
|
||||
empty.style.display='none';
|
||||
items.forEach(it=>{
|
||||
const params = new URLSearchParams();
|
||||
if (it.q) params.set('q', it.q);
|
||||
const sel = it.selected || {};
|
||||
Object.keys(sel).forEach(k=>{ if (sel[k]) params.set(k, 'on'); });
|
||||
|
||||
const li = document.createElement('li');
|
||||
const selectedList = Object.keys(sel).filter(k=>sel[k]).join(', ');
|
||||
li.innerHTML = `<a href="{% url 'search' %}?${params.toString()}"><strong>${it.q || '(blank)'}</strong></a>
|
||||
<span class="muted">— ${Object.keys(sel).filter(k=>sel[k]).join(', ')}</span>`;
|
||||
histList.appendChild(li);
|
||||
<span class="muted">— ${selectedList}</span>`;
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
fetch("{% url 'api_get_search_history' %}").then(r=>r.json()).then(j=>{
|
||||
if (j.ok) renderHistory(j.items);
|
||||
}).catch(()=>{});
|
||||
|
||||
// --- Recently viewed: fetch + render (keeps snippet + fixed link) ---
|
||||
const rvList = document.getElementById('recentViewsList');
|
||||
const rvEmpty = document.getElementById('recentViewsEmpty');
|
||||
// Recently viewed
|
||||
function renderRecent(items){
|
||||
rvList.innerHTML = '';
|
||||
if (!items || !items.length){ rvEmpty.style.display='block'; return; }
|
||||
rvEmpty.style.display = 'none';
|
||||
const list = document.getElementById('recentViewsList');
|
||||
const empty = document.getElementById('recentViewsEmpty');
|
||||
list.innerHTML = '';
|
||||
if (!items || !items.length){ empty.style.display='block'; return; }
|
||||
empty.style.display='none';
|
||||
items.forEach(it=>{
|
||||
const url = entryUrlFor(it.entry_id);
|
||||
const snippet = (it.snippet && it.snippet.trim())
|
||||
@ -188,76 +158,25 @@
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<a href="${url}">${snippet}</a>
|
||||
<span class="muted"> — ${when.toLocaleString()}</span>`;
|
||||
rvList.appendChild(li);
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
fetch("{% url 'api_get_recent_views' %}").then(r=>r.json()).then(j=>{
|
||||
if (j.ok) renderRecent(j.items);
|
||||
}).catch(()=>{});
|
||||
|
||||
// --- Log searches reliably using Beacon on submit ---
|
||||
const searchForm = document.querySelector('form.search-form');
|
||||
if (searchForm){
|
||||
searchForm.addEventListener('submit', ()=>{
|
||||
try{
|
||||
const fd = new FormData(searchForm);
|
||||
const data = new URLSearchParams();
|
||||
data.append('q', (fd.get('q') || '').trim());
|
||||
['subject','illustration','application','scripture_raw','source','talk_title','talk_number','entry_code']
|
||||
.forEach(k=>{
|
||||
if (fd.get(k)) data.append(`sel[${k}]`, 'on');
|
||||
});
|
||||
const blob = new Blob([data.toString()], {type:'application/x-www-form-urlencoded'});
|
||||
navigator.sendBeacon("{% url 'api_log_search' %}", blob);
|
||||
}catch(_){}
|
||||
// Dropdown toggle for History
|
||||
document.querySelectorAll('.dropdown-toggle').forEach(btn=>{
|
||||
btn.addEventListener('click', ()=>{
|
||||
const target = document.querySelector(btn.dataset.target);
|
||||
if (target){
|
||||
target.classList.toggle('open');
|
||||
btn.classList.toggle('open');
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
});
|
||||
|
||||
<!-- Styles for the help panel + collapsible -->
|
||||
<style>
|
||||
.help-panel{
|
||||
display:none;
|
||||
margin-top:10px;
|
||||
background:#fff;
|
||||
border:1px solid var(--border);
|
||||
border-radius:12px;
|
||||
padding:14px;
|
||||
box-shadow:0 4px 16px rgba(0,0,0,.06);
|
||||
font-size:14px;
|
||||
line-height:1.6;
|
||||
}
|
||||
.help-panel.open{ display:block; }
|
||||
.help-panel code{ background:#f3f4f6; padding:2px 4px; border-radius:4px; font-size:90%; }
|
||||
.help-panel h3{ margin:0 0 8px; font-size:16px; }
|
||||
.help-panel ul{ margin:0 0 8px 18px; }
|
||||
|
||||
/* Collapsible History bar */
|
||||
.collapse-bar{
|
||||
width:100%;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap:10px;
|
||||
padding:12px 14px;
|
||||
background:#fff;
|
||||
border:0;
|
||||
border-bottom:1px solid var(--border);
|
||||
border-radius:12px 12px 0 0;
|
||||
cursor:pointer;
|
||||
font-weight:600;
|
||||
text-align:left;
|
||||
}
|
||||
.collapse-bar:focus{ outline:2px solid #c7d2fe; outline-offset:2px; }
|
||||
.collapse-body{
|
||||
background:#fff;
|
||||
border-radius:0 0 12px 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Inline JS to toggle the Help panel (unchanged) -->
|
||||
<script>
|
||||
// Help panel toggle (unchanged)
|
||||
document.addEventListener('click', function(e){
|
||||
const btn = e.target.closest('.help-toggle');
|
||||
if (btn) {
|
||||
@ -272,5 +191,36 @@
|
||||
if (!clickedInside) panel.classList.remove('open');
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Help panel */
|
||||
.help-panel{
|
||||
display:none;
|
||||
margin-top:10px;
|
||||
background:#fff;
|
||||
border:1px solid var(--border);
|
||||
border-radius:12px;
|
||||
padding:14px;
|
||||
box-shadow:0 4px 16px rgba(0,0,0,.06);
|
||||
font-size:14px;
|
||||
}
|
||||
.help-panel.open{ display:block; }
|
||||
|
||||
/* Dropdown panel */
|
||||
.dropdown-panel { display:none; padding:12px; }
|
||||
.dropdown-panel.open { display:block; }
|
||||
|
||||
/* Chevron style sized for heading */
|
||||
.page-title .chevron {
|
||||
font-size: 0.9em;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
transition: transform 0.2s ease;
|
||||
display:inline-block;
|
||||
}
|
||||
.page-title.open .chevron { transform: rotate(180deg); }
|
||||
.dropdown-toggle { cursor:pointer; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue
Block a user