diff --git a/app.js b/app.js index 297daaf..8681413 100644 --- a/app.js +++ b/app.js @@ -14,8 +14,9 @@ let sortCol = 'name'; let sortDir = 'asc'; // editing state -let editingTuneId = null; // null = add mode -let editingEntryId = null; // null = no existing instrument entry +let editingTuneId = null; // null = add mode +let editingEntryId = null; // null = no existing instrument entry +let editingOriginalRefIds = []; // ref IDs present when edit modal opened // notes modal state let notesTuneId = null; @@ -499,8 +500,9 @@ function pillStatus(status, extraClass = '') { btnAddTune.addEventListener('click', openAdd); function openAdd() { - editingTuneId = null; - editingEntryId = null; + editingTuneId = null; + editingEntryId = null; + editingOriginalRefIds = []; $('modal-title').textContent = 'Add tune'; tuneForm.reset(); refsList.innerHTML = ''; @@ -552,6 +554,7 @@ async function openEdit(tuneId, entryId) { tuneForm.elements.inst_difficulty.value = entry.difficulty ?? ''; } + editingOriginalRefIds = (fullTune.references ?? []).map(r => r.id); (fullTune.references ?? []).forEach(r => addRefRow(r.link, r.site?.name ?? '', r.id, r.site_id, r.musicians ?? [])); } @@ -716,6 +719,35 @@ async function saveTune() { }); } } + + // Sync references + const formRows = [...refsList.querySelectorAll('.ref-row')]; + const formRefIds = new Set(formRows.map(r => r.dataset.refId).filter(Boolean)); + + // Delete refs removed from the form + for (const refId of editingOriginalRefIds) { + if (!formRefIds.has(String(refId))) { + await apiFetch(`/tunes/${tuneId}/references/${refId}`, { method: 'DELETE' }); + } + } + + // Create new refs; patch existing ones (link + site) + for (const row of formRows) { + const refId = row.dataset.refId; + const link = row.querySelector('.ref-link').value.trim() || null; + const siteId = row.querySelector('.ref-site').value ? parseInt(row.querySelector('.ref-site').value) : null; + if (!link && !siteId) continue; + if (refId) { + await apiFetch(`/tunes/${tuneId}/references/${refId}`, { + method: 'PATCH', body: JSON.stringify({ link, site_id: siteId }), + }); + } else { + const musicians = row._getMusicians ? row._getMusicians() : []; + await apiFetch(`/tunes/${tuneId}/references`, { + method: 'POST', body: JSON.stringify({ link, site_id: siteId, musicians }), + }); + } + } } tuneModal.close(); @@ -901,12 +933,17 @@ async function openRefsModal(tuneId) { 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(' · '); + const musicians = r.musicians?.map(m => m.name).join(', ') ?? ''; + const siteName = r.site?.name ?? ''; + const meta = [siteName, musicians].filter(Boolean).join(' · '); + // Use meta as link text; fall back to hostname so raw URL is never shown + let displayText = meta; + if (!displayText && r.link) { + try { displayText = new URL(r.link).hostname.replace(/^www\./, ''); } catch { displayText = 'link'; } + } + if (!displayText) displayText = 'link'; item.innerHTML = ` - ${esc(r.link ?? r.site?.name ?? 'link')} - ${meta ? `${esc(meta)}` : ''}`; + ${esc(displayText)}`; refsModalList.appendChild(item); }); } diff --git a/style.css b/style.css index 38a12c6..e96f8e8 100644 --- a/style.css +++ b/style.css @@ -787,7 +787,6 @@ fieldset.active .fs-body { display: contents; } .ref-link-anchor { color: var(--accent); font-size: .875rem; - word-break: break-all; text-decoration: none; } .ref-link-anchor:hover { text-decoration: underline; }