Collapse to learn, review, callible into one column

This commit is contained in:
Ian Keane 2026-06-11 12:54:08 -04:00
parent cccfba0712
commit 58c4dc08f3
3 changed files with 51 additions and 34 deletions

46
app.js
View file

@ -241,9 +241,7 @@ const filters = {
modal: null,
instrument: null,
tuning: null,
callable: null,
review: null,
to_learn: null,
status: null,
difficulty: null,
};
@ -251,8 +249,8 @@ const popover = $('filter-popover');
const popoverInner = $('filter-popover-inner');
let activeFilterKey = null;
const BOOL_FILTERS = new Set(['modal', 'callable', 'review', 'to_learn']);
const DROPDOWN_FILTERS = new Set(['source', 'instrument', 'tuning', 'difficulty']);
const BOOL_FILTERS = new Set(['modal']);
const DROPDOWN_FILTERS = new Set(['source', 'instrument', 'tuning', 'difficulty', 'status']);
function openFilterPopover(key, thEl) {
if (activeFilterKey === key) { closeFilterPopover(); return; }
@ -267,7 +265,7 @@ function openFilterPopover(key, thEl) {
if (filters[key] === true) { cb.checked = true; cb.indeterminate = false; }
else if (filters[key] === false) { cb.checked = false; cb.indeterminate = true; }
else { cb.checked = false; cb.indeterminate = false; }
const names = { modal: 'Modal', callable: 'Callable', review: 'Needs review', to_learn: 'To learn' };
const names = { modal: 'Modal' };
label.appendChild(cb);
label.appendChild(document.createTextNode(names[key] ?? key));
popoverInner.appendChild(label);
@ -328,6 +326,7 @@ function buildDropdownOptions(key) {
if (key === 'instrument') return [...none, ...allInstruments.map(i => [String(i.id), i.name])];
if (key === 'tuning') return [...none, ...allTunings.map(t => [String(t.id), t.name])];
if (key === 'difficulty') return [...none, ['easy', 'Easy'], ['hard', 'Hard']];
if (key === 'status') return [...none, ['callable', 'Callable'], ['review', 'Needs Review'], ['to_learn', 'To Learn']];
return none;
}
@ -367,9 +366,7 @@ function rowMatchesFilters(tune, entry) {
if (filters.instrument && (!entry || String(entry.instrument_id) !== filters.instrument)) return false;
if (entry) {
if (filters.tuning && String(entry.tuning_id) !== filters.tuning) return false;
if (filters.callable !== null && entry.callable !== filters.callable) return false;
if (filters.review !== null && entry.review !== filters.review) return false;
if (filters.to_learn !== null && entry.to_learn !== filters.to_learn) return false;
if (filters.status && entry.status !== filters.status) return false;
if (filters.difficulty && entry.difficulty?.toLowerCase() !== filters.difficulty) return false;
}
return true;
@ -420,11 +417,7 @@ function renderTable() {
? (tune.modal ? `${tune.key} modal` : tune.key)
: (tune.modal ? 'modal' : '');
const metaRaw = [instStr, keyStr].filter(Boolean).join(' · ');
const mobPills = entry ? [
`<span class="pill mob-pill ${entry.callable ? 'pill-yes' : 'pill-dim'}">Call</span>`,
`<span class="pill mob-pill ${entry.review ? 'pill-warn' : 'pill-dim'}">Rev</span>`,
`<span class="pill mob-pill ${entry.to_learn ? 'pill-warn' : 'pill-dim'}">Lrn</span>`,
].join('') : '';
const mobPills = entry ? pillStatus(entry.status, 'mob-pill') : '';
const mobActions = [
hasNotes ? `<button class="action-btn btn-notes" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}">📝</button>` : '',
tune.references?.length > 0 ? `<button class="action-btn btn-show-refs" data-tune-id="${tune.id}">🔗</button>` : '',
@ -439,9 +432,7 @@ function renderTable() {
<td>${tune.modal ? '<span class="pill pill-yes">Yes</span>' : '<span class="pill" style="background:#222">—</span>'}</td>
<td>${esc(entry?.instrument_name ?? '')}</td>
<td>${esc(entry?.tuning ?? '')}</td>
<td>${pillBool(entry?.callable)}</td>
<td>${pillBool(entry?.review, true)}</td>
<td>${pillBool(entry?.to_learn, false, true)}</td>
<td>${pillStatus(entry?.status)}</td>
<td>${hasNotes
? `<button class="action-btn btn-notes" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}">📝</button>`
: ''}</td>
@ -486,6 +477,14 @@ function pillBool(val, warnIfTrue = false, neutralIfTrue = false) {
return '<span class="pill pill-no">No</span>';
}
function pillStatus(status, extraClass = '') {
const c = extraClass ? ' ' + extraClass : '';
if (status === 'callable') return `<span class="pill pill-yes${c}">Callable</span>`;
if (status === 'review') return `<span class="pill pill-warn${c}">Needs Review</span>`;
if (status === 'to_learn') return `<span class="pill pill-to-learn${c}">To Learn</span>`;
return `<span class="pill pill-dim${c}">—</span>`;
}
// ── Add / Edit modal ──────────────────────────────────────────
btnAddTune.addEventListener('click', openAdd);
@ -524,9 +523,7 @@ function openEdit(tuneId, entryId) {
tuneForm.elements.inst_tuning.value = entry.tuning ?? '';
tuneForm.elements.inst_learned_from.value = entry.learned_from ?? '';
tuneForm.elements.inst_date_learned.value = entry.date_learned ?? '';
tuneForm.elements.inst_callable.checked = !!entry.callable;
tuneForm.elements.inst_review.checked = !!entry.review;
tuneForm.elements.inst_to_learn.checked = !!entry.to_learn;
tuneForm.elements.inst_status.value = entry.status ?? '';
tuneForm.elements.inst_difficulty.value = entry.difficulty ?? '';
}
@ -657,9 +654,7 @@ async function saveTune() {
tuning_id: resolveTuningId(f.inst_tuning.value),
learned_from_id: resolveMusicianId(f.inst_learned_from.value),
date_learned: f.inst_date_learned.value || null,
callable: f.inst_callable.checked,
review: f.inst_review.checked,
to_learn: f.inst_to_learn.checked,
status: f.inst_status.value || null,
difficulty: f.inst_difficulty.value || null,
} : null;
@ -891,7 +886,7 @@ function callGetEntries() {
const result = [];
for (const tune of allTunes)
for (const entry of tune.instruments)
if (entry[callMode]) result.push({ tune, entry });
if (entry.status === callMode) result.push({ tune, entry });
return result;
}
@ -986,8 +981,7 @@ function callSelect(value, modal) {
callStep = 2;
renderCallStep();
} else {
filters.callable = callMode === 'callable' ? true : null;
filters.to_learn = callMode === 'to_learn' ? true : null;
filters.status = callMode;
filters.instrument = callInstrumentId !== null ? String(callInstrumentId) : null;
filters.tuning = callTuningId !== null ? String(callTuningId) : null;
filters.key = value;