Slim dict for faster loading
This commit is contained in:
parent
7f19e11885
commit
5812d3cc76
1 changed files with 93 additions and 63 deletions
156
app.js
156
app.js
|
|
@ -416,7 +416,7 @@ function renderTable() {
|
|||
emptyMsg.hidden = rows.length > 0;
|
||||
|
||||
rows.forEach(({ tune, entry }) => {
|
||||
const hasNotes = (tune.notes?.length > 0) || (entry?.notes?.length > 0);
|
||||
const hasNotes = tune.has_notes ?? false;
|
||||
|
||||
// ── Mobile 2-line card data ──────────────────────────────────
|
||||
const nameRaw = tune.name ?? '';
|
||||
|
|
@ -431,7 +431,7 @@ function renderTable() {
|
|||
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>` : '',
|
||||
(tune.reference_count ?? 0) > 0 ? `<button class="action-btn btn-show-refs" data-tune-id="${tune.id}">🔗</button>` : '',
|
||||
`<button class="action-btn btn-edit-row" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}" ${unlocked ? '' : 'hidden'}>✏️</button>`,
|
||||
].join('');
|
||||
|
||||
|
|
@ -446,7 +446,7 @@ function renderTable() {
|
|||
<td>${hasNotes
|
||||
? `<button class="action-btn btn-notes" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}">📝</button>`
|
||||
: ''}</td>
|
||||
<td>${tune.references?.length > 0
|
||||
<td>${(tune.reference_count ?? 0) > 0
|
||||
? `<button class="action-btn btn-show-refs" data-tune-id="${tune.id}" title="References">🔗</button>`
|
||||
: ''}</td>
|
||||
<td><button class="action-btn btn-edit-row"
|
||||
|
|
@ -512,22 +512,37 @@ function openAdd() {
|
|||
tuneModal.showModal();
|
||||
}
|
||||
|
||||
function openEdit(tuneId, entryId) {
|
||||
async function openEdit(tuneId, entryId) {
|
||||
editingTuneId = tuneId;
|
||||
editingEntryId = entryId;
|
||||
|
||||
const tune = allTunes.find(t => t.id === tuneId);
|
||||
const entry = entryId ? tune?.instruments.find(e => e.id === entryId) : null;
|
||||
|
||||
$('modal-title').textContent = `Edit: ${tune?.name ?? ''}`;
|
||||
// Open modal immediately — name from slim data gives instant feedback
|
||||
const slimTune = allTunes.find(t => t.id === tuneId);
|
||||
$('modal-title').textContent = `Edit: ${slimTune?.name ?? ''}`;
|
||||
tuneForm.reset();
|
||||
refsList.innerHTML = '';
|
||||
btnDeleteTune.hidden = false;
|
||||
btnEditNotes.hidden = false;
|
||||
tuneModal.showModal();
|
||||
|
||||
tuneForm.elements.name.value = tune?.name ?? '';
|
||||
tuneForm.elements.key.value = tune?.key ?? '';
|
||||
tuneForm.elements.modal.checked = !!tune?.modal;
|
||||
sourceInput.value = tune?.source?.name ?? '';
|
||||
// Fetch full tune for references and confirmed field values
|
||||
let fullTune;
|
||||
try {
|
||||
fullTune = await apiFetch(`/tunes/${tuneId}`);
|
||||
} catch (e) {
|
||||
setStatus(e.message, true);
|
||||
tuneModal.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = entryId ? fullTune.instruments.find(e => e.id === entryId) : null;
|
||||
|
||||
$('modal-title').textContent = `Edit: ${fullTune.name ?? ''}`;
|
||||
tuneForm.elements.name.value = fullTune.name ?? '';
|
||||
tuneForm.elements.key.value = fullTune.key ?? '';
|
||||
tuneForm.elements.modal.checked = !!fullTune.modal;
|
||||
sourceInput.value = fullTune.source?.name ?? '';
|
||||
|
||||
// Instrument entry
|
||||
populateInstrumentSelect(entry?.instrument_id ?? null);
|
||||
if (entry) {
|
||||
tuneForm.elements.inst_tuning.value = entry.tuning ?? '';
|
||||
|
|
@ -537,13 +552,7 @@ function openEdit(tuneId, entryId) {
|
|||
tuneForm.elements.inst_difficulty.value = entry.difficulty ?? '';
|
||||
}
|
||||
|
||||
// References
|
||||
refsList.innerHTML = '';
|
||||
(tune?.references ?? []).forEach(r => addRefRow(r.link, r.site?.name ?? '', r.id, r.site_id, r.musicians ?? []));
|
||||
|
||||
btnDeleteTune.hidden = false;
|
||||
btnEditNotes.hidden = false;
|
||||
tuneModal.showModal();
|
||||
(fullTune.references ?? []).forEach(r => addRefRow(r.link, r.site?.name ?? '', r.id, r.site_id, r.musicians ?? []));
|
||||
}
|
||||
|
||||
// ── Musician chip picker (shared by ref rows + bulk form) ──────
|
||||
|
|
@ -739,22 +748,30 @@ async function openNotesModal(tuneId, entryId) {
|
|||
notesTuneId = tuneId;
|
||||
notesEntryId = entryId;
|
||||
|
||||
const tune = allTunes.find(t => t.id === tuneId);
|
||||
const entry = entryId ? tune?.instruments.find(e => e.id === entryId) : null;
|
||||
|
||||
$('notes-modal-title').textContent = `Notes — ${tune?.name ?? ''}`;
|
||||
$('inst-notes-heading').textContent = entry
|
||||
? `${entry.instrument_name} notes`
|
||||
: 'Instrument notes';
|
||||
$('inst-notes-section').hidden = !entry;
|
||||
|
||||
renderTuneNotes(tune?.notes ?? []);
|
||||
renderInstNotes(entry?.notes ?? []);
|
||||
|
||||
$('btn-add-tune-note').hidden = !unlocked;
|
||||
$('btn-add-inst-note').hidden = !unlocked || !entry;
|
||||
|
||||
// Open immediately with name from slim data
|
||||
const slimTune = allTunes.find(t => t.id === tuneId);
|
||||
$('notes-modal-title').textContent = `Notes — ${slimTune?.name ?? ''}`;
|
||||
$('tune-notes-list').innerHTML = '';
|
||||
$('inst-notes-list').innerHTML = '';
|
||||
$('inst-notes-section').hidden = !entryId;
|
||||
$('btn-add-tune-note').hidden = !unlocked;
|
||||
$('btn-add-inst-note').hidden = !unlocked || !entryId;
|
||||
notesModal.showModal();
|
||||
|
||||
// Fetch full tune for note data
|
||||
try {
|
||||
const fullTune = await apiFetch(`/tunes/${tuneId}`);
|
||||
const entry = entryId ? fullTune.instruments.find(e => e.id === entryId) : null;
|
||||
|
||||
$('inst-notes-heading').textContent = entry ? `${entry.instrument_name} notes` : 'Instrument notes';
|
||||
$('inst-notes-section').hidden = !entry;
|
||||
$('btn-add-inst-note').hidden = !unlocked || !entry;
|
||||
|
||||
renderTuneNotes(fullTune.notes ?? []);
|
||||
renderInstNotes(entry?.notes ?? []);
|
||||
} catch (e) {
|
||||
setStatus(e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
function renderTuneNotes(notes) {
|
||||
|
|
@ -848,11 +865,20 @@ async function deleteInstNote(noteId) {
|
|||
}
|
||||
|
||||
async function reloadNotesModal() {
|
||||
await loadAll();
|
||||
const tune = allTunes.find(t => t.id === notesTuneId);
|
||||
const entry = notesEntryId ? tune?.instruments.find(e => e.id === notesEntryId) : null;
|
||||
renderTuneNotes(tune?.notes ?? []);
|
||||
renderInstNotes(entry?.notes ?? []);
|
||||
try {
|
||||
const fullTune = await apiFetch(`/tunes/${notesTuneId}`);
|
||||
const entry = notesEntryId ? fullTune.instruments.find(e => e.id === notesEntryId) : null;
|
||||
renderTuneNotes(fullTune.notes ?? []);
|
||||
renderInstNotes(entry?.notes ?? []);
|
||||
// Keep has_notes in sync so the 📝 button stays accurate
|
||||
const idx = allTunes.findIndex(t => t.id === notesTuneId);
|
||||
if (idx >= 0) {
|
||||
allTunes[idx].has_notes = !!fullTune.notes?.length
|
||||
|| fullTune.instruments.some(e => !!e.notes?.length);
|
||||
}
|
||||
} catch (e) {
|
||||
setStatus(e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
$('btn-notes-close').addEventListener('click', () => notesModal.close());
|
||||
|
|
@ -860,29 +886,33 @@ $('btn-notes-close').addEventListener('click', () => notesModal.close());
|
|||
// ── References display modal ────────────────────────────────────
|
||||
$('btn-refs-modal-close').addEventListener('click', () => refsModal.close());
|
||||
|
||||
function openRefsModal(tuneId) {
|
||||
async function openRefsModal(tuneId) {
|
||||
const tune = allTunes.find(t => t.id === tuneId);
|
||||
$('refs-modal-title').textContent = `References — ${tune?.name ?? ''}`;
|
||||
refsModalList.innerHTML = '';
|
||||
|
||||
const refs = tune?.references ?? [];
|
||||
if (refs.length === 0) {
|
||||
refsModalList.innerHTML = '<p style="color:var(--muted);font-size:.875rem">No references.</p>';
|
||||
} else {
|
||||
refs.forEach(r => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'refs-modal-item';
|
||||
const musicians = r.musicians?.map(m => m.name).join(', ') ?? '';
|
||||
const siteName = r.site?.name ?? '';
|
||||
const meta = [siteName, musicians].filter(Boolean).join(' · ');
|
||||
item.innerHTML = `
|
||||
<a href="${esc(r.link ?? '')}" target="_blank" rel="noopener" class="ref-link-anchor">${esc(r.link ?? r.site?.name ?? 'link')}</a>
|
||||
${meta ? `<span class="ref-meta">${esc(meta)}</span>` : ''}`;
|
||||
refsModalList.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
refsModalList.innerHTML = '<p style="color:var(--muted);font-size:.875rem">Loading…</p>';
|
||||
refsModal.showModal();
|
||||
|
||||
try {
|
||||
const refs = await apiFetch(`/tunes/${tuneId}/references`);
|
||||
refsModalList.innerHTML = '';
|
||||
if (refs.length === 0) {
|
||||
refsModalList.innerHTML = '<p style="color:var(--muted);font-size:.875rem">No references.</p>';
|
||||
} else {
|
||||
refs.forEach(r => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'refs-modal-item';
|
||||
const musicians = r.musicians?.map(m => m.name).join(', ') ?? '';
|
||||
const siteName = r.site?.name ?? '';
|
||||
const meta = [siteName, musicians].filter(Boolean).join(' · ');
|
||||
item.innerHTML = `
|
||||
<a href="${esc(r.link ?? '')}" target="_blank" rel="noopener" class="ref-link-anchor">${esc(r.link ?? r.site?.name ?? 'link')}</a>
|
||||
${meta ? `<span class="ref-meta">${esc(meta)}</span>` : ''}`;
|
||||
refsModalList.appendChild(item);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
refsModalList.innerHTML = `<p style="color:#eb5757;font-size:.875rem">${esc(e.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Call / Learn mode ────────────────────────────────────────────
|
||||
|
|
@ -1086,8 +1116,8 @@ function getBulkRefsFiltered() {
|
|||
if (search && !(t.name ?? '').toLowerCase().includes(search)) return false;
|
||||
if (srcId && t.source_id !== parseInt(srcId)) return false;
|
||||
if (instId && !t.instruments.some(e => e.instrument_id === parseInt(instId))) return false;
|
||||
if (hasRefs === 'no' && t.references.length > 0) return false;
|
||||
if (hasRefs === 'yes' && t.references.length === 0) return false;
|
||||
if (hasRefs === 'no' && (t.reference_count ?? 0) > 0) return false;
|
||||
if (hasRefs === 'yes' && (t.reference_count ?? 0) === 0) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -1124,7 +1154,7 @@ function renderBulkRefsList() {
|
|||
row.dataset.tuneId = tune.id;
|
||||
|
||||
const instruments = tune.instruments.map(e => e.instrument_name).filter(Boolean).join(', ');
|
||||
const refCount = tune.references.length;
|
||||
const refCount = tune.reference_count ?? 0;
|
||||
const refPill = refCount > 0
|
||||
? `<span class="pill pill-yes">${refCount} ref${refCount !== 1 ? 's' : ''}</span>`
|
||||
: `<span class="pill" style="background:#222">0</span>`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue