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