Add term column
This commit is contained in:
parent
52eb0f6189
commit
dba48e90b7
2 changed files with 31 additions and 1 deletions
23
app.js
23
app.js
|
|
@ -10,6 +10,7 @@ let allMusicians = [];
|
||||||
let allInstruments = [];
|
let allInstruments = [];
|
||||||
let allTunings = [];
|
let allTunings = [];
|
||||||
let allReferenceSites = [];
|
let allReferenceSites = [];
|
||||||
|
let allTerms = [];
|
||||||
let sortCol = 'name';
|
let sortCol = 'name';
|
||||||
let sortDir = 'asc';
|
let sortDir = 'asc';
|
||||||
|
|
||||||
|
|
@ -43,6 +44,7 @@ const sourceDatalist = $('source-list');
|
||||||
const musicianDatalist = $('musician-list');
|
const musicianDatalist = $('musician-list');
|
||||||
const instrumentSelect = $('instrument-select');
|
const instrumentSelect = $('instrument-select');
|
||||||
const tuningDatalist = $('tuning-list');
|
const tuningDatalist = $('tuning-list');
|
||||||
|
const termDatalist = $('term-list');
|
||||||
const btnDeleteTune = $('btn-delete-tune');
|
const btnDeleteTune = $('btn-delete-tune');
|
||||||
const btnEditNotes = $('btn-edit-notes');
|
const btnEditNotes = $('btn-edit-notes');
|
||||||
const btnBulkEdit = $('btn-bulk-edit');
|
const btnBulkEdit = $('btn-bulk-edit');
|
||||||
|
|
@ -116,18 +118,20 @@ function setStatus(msg, isError = false) {
|
||||||
async function loadAll() {
|
async function loadAll() {
|
||||||
setStatus('Loading…');
|
setStatus('Loading…');
|
||||||
try {
|
try {
|
||||||
[allTunes, allSources, allMusicians, allInstruments, allTunings, allReferenceSites] = await Promise.all([
|
[allTunes, allSources, allMusicians, allInstruments, allTunings, allReferenceSites, allTerms] = await Promise.all([
|
||||||
apiFetch('/tunes/'),
|
apiFetch('/tunes/'),
|
||||||
apiFetch('/sources/'),
|
apiFetch('/sources/'),
|
||||||
apiFetch('/musicians/'),
|
apiFetch('/musicians/'),
|
||||||
apiFetch('/instruments'),
|
apiFetch('/instruments'),
|
||||||
apiFetch('/tunings/'),
|
apiFetch('/tunings/'),
|
||||||
apiFetch('/reference_sites/'),
|
apiFetch('/reference_sites/'),
|
||||||
|
apiFetch('/terms/'),
|
||||||
]);
|
]);
|
||||||
populateFilterOptions();
|
populateFilterOptions();
|
||||||
populateDatalist(sourceDatalist, allSources);
|
populateDatalist(sourceDatalist, allSources);
|
||||||
populateDatalist(musicianDatalist, allMusicians);
|
populateDatalist(musicianDatalist, allMusicians);
|
||||||
populateDatalist(tuningDatalist, allTunings);
|
populateDatalist(tuningDatalist, allTunings);
|
||||||
|
populateDatalist(termDatalist, allTerms);
|
||||||
populateDatalist(refSiteDatalist, allReferenceSites);
|
populateDatalist(refSiteDatalist, allReferenceSites);
|
||||||
populateInstrumentSelect();
|
populateInstrumentSelect();
|
||||||
renderTable();
|
renderTable();
|
||||||
|
|
@ -204,6 +208,12 @@ $('btn-quick-add-tuning').addEventListener('click', async () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('btn-quick-add-term').addEventListener('click', async () => {
|
||||||
|
await quickAdd('/terms/', 'New term name:', allTerms, termDatalist, item => {
|
||||||
|
tuneForm.elements.inst_term.value = item.name;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('btn-quick-add-instrument').addEventListener('click', async () => {
|
$('btn-quick-add-instrument').addEventListener('click', async () => {
|
||||||
const item = await quickAdd('/instruments', 'New instrument name:', allInstruments, null);
|
const item = await quickAdd('/instruments', 'New instrument name:', allInstruments, null);
|
||||||
if (item) {
|
if (item) {
|
||||||
|
|
@ -231,6 +241,11 @@ function resolveTuningId(name) {
|
||||||
return allTunings.find(x => x.name.toLowerCase() === name.toLowerCase())?.id ?? null;
|
return allTunings.find(x => x.name.toLowerCase() === name.toLowerCase())?.id ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveTermId(name) {
|
||||||
|
if (!name) return null;
|
||||||
|
return allTerms.find(x => x.name.toLowerCase() === name.toLowerCase())?.id ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Rows: one per instrument entry ────────────────────────────
|
// ── Rows: one per instrument entry ────────────────────────────
|
||||||
// For tunes with no instrument entries, emit one row with empty instrument cols
|
// For tunes with no instrument entries, emit one row with empty instrument cols
|
||||||
function buildRows(tunes) {
|
function buildRows(tunes) {
|
||||||
|
|
@ -550,6 +565,7 @@ async function openEdit(tuneId, entryId) {
|
||||||
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 ?? '';
|
||||||
|
tuneForm.elements.inst_term.value = entry.term ?? '';
|
||||||
tuneForm.elements.inst_learned_from.value = entry.learned_from ?? '';
|
tuneForm.elements.inst_learned_from.value = entry.learned_from ?? '';
|
||||||
tuneForm.elements.inst_date_learned.value = entry.date_learned ?? '';
|
tuneForm.elements.inst_date_learned.value = entry.date_learned ?? '';
|
||||||
tuneForm.elements.inst_status.value = entry.status ?? '';
|
tuneForm.elements.inst_status.value = entry.status ?? '';
|
||||||
|
|
@ -708,6 +724,7 @@ async function saveTune() {
|
||||||
const instBody = instId ? {
|
const instBody = instId ? {
|
||||||
instrument_id: instId,
|
instrument_id: instId,
|
||||||
tuning_id: resolveTuningId(f.inst_tuning.value),
|
tuning_id: resolveTuningId(f.inst_tuning.value),
|
||||||
|
term_id: resolveTermId(f.inst_term.value),
|
||||||
learned_from_id: resolveMusicianId(f.inst_learned_from.value),
|
learned_from_id: resolveMusicianId(f.inst_learned_from.value),
|
||||||
date_learned: f.inst_date_learned.value || null,
|
date_learned: f.inst_date_learned.value || null,
|
||||||
status: f.inst_status.value || null,
|
status: f.inst_status.value || null,
|
||||||
|
|
@ -1432,6 +1449,7 @@ async function importCSVRows(rows) {
|
||||||
const tunCache = new Map(); // name.lower → id (tunings)
|
const tunCache = new Map(); // name.lower → id (tunings)
|
||||||
const musCache = new Map(); // name.lower → id (musicians)
|
const musCache = new Map(); // name.lower → id (musicians)
|
||||||
const instCache = new Map(); // name.lower → id (instruments)
|
const instCache = new Map(); // name.lower → id (instruments)
|
||||||
|
const termCache = new Map(); // name.lower → id (terms)
|
||||||
const tuneCache = new Map(); // name.lower → id (tunes created this run)
|
const tuneCache = new Map(); // name.lower → id (tunes created this run)
|
||||||
const results = { created: 0, skipped: 0, errors: [] };
|
const results = { created: 0, skipped: 0, errors: [] };
|
||||||
|
|
||||||
|
|
@ -1455,6 +1473,7 @@ async function importCSVRows(rows) {
|
||||||
const tuningId = await resolveOrCreate('/tunings/', allTunings, tunCache, row.tuning?.trim() || null);
|
const tuningId = await resolveOrCreate('/tunings/', allTunings, tunCache, row.tuning?.trim() || null);
|
||||||
const learnedFromId = await resolveOrCreate('/musicians/', allMusicians, musCache, row.learned_from?.trim() || null);
|
const learnedFromId = await resolveOrCreate('/musicians/', allMusicians, musCache, row.learned_from?.trim() || null);
|
||||||
const instId = await resolveOrCreate('/instruments', allInstruments, instCache, row.instrument?.trim() || null);
|
const instId = await resolveOrCreate('/instruments', allInstruments, instCache, row.instrument?.trim() || null);
|
||||||
|
const termId = await resolveOrCreate('/terms/', allTerms, termCache, row.term?.trim() || null);
|
||||||
|
|
||||||
// Find existing tune or create new
|
// Find existing tune or create new
|
||||||
const lower = tuneName.toLowerCase();
|
const lower = tuneName.toLowerCase();
|
||||||
|
|
@ -1481,6 +1500,7 @@ async function importCSVRows(rows) {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
instrument_id: instId,
|
instrument_id: instId,
|
||||||
tuning_id: tuningId,
|
tuning_id: tuningId,
|
||||||
|
term_id: termId,
|
||||||
learned_from_id: learnedFromId,
|
learned_from_id: learnedFromId,
|
||||||
date_learned: row.date_learned?.trim() || null,
|
date_learned: row.date_learned?.trim() || null,
|
||||||
status,
|
status,
|
||||||
|
|
@ -1537,6 +1557,7 @@ function attachTabAutocomplete(input, datalist) {
|
||||||
|
|
||||||
attachTabAutocomplete(sourceInput, sourceDatalist);
|
attachTabAutocomplete(sourceInput, sourceDatalist);
|
||||||
attachTabAutocomplete(tuneForm.elements.inst_tuning, tuningDatalist);
|
attachTabAutocomplete(tuneForm.elements.inst_tuning, tuningDatalist);
|
||||||
|
attachTabAutocomplete(tuneForm.elements.inst_term, termDatalist);
|
||||||
attachTabAutocomplete(tuneForm.elements.inst_learned_from, musicianDatalist);
|
attachTabAutocomplete(tuneForm.elements.inst_learned_from, musicianDatalist);
|
||||||
|
|
||||||
// ── Boot ─────────────────────────────────────────────────────
|
// ── Boot ─────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,14 @@
|
||||||
<button type="button" class="quick-add-btn" id="btn-quick-add-tuning" title="Add new tuning">+</button>
|
<button type="button" class="quick-add-btn" id="btn-quick-add-tuning" title="Add new tuning">+</button>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label>Term
|
||||||
|
<span class="autocomplete-row">
|
||||||
|
<input name="inst_term" type="text"
|
||||||
|
list="term-list" placeholder="— none —" autocomplete="off" />
|
||||||
|
<datalist id="term-list"></datalist>
|
||||||
|
<button type="button" class="quick-add-btn" id="btn-quick-add-term" title="Add new term">+</button>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
<label>Learned from
|
<label>Learned from
|
||||||
<span class="autocomplete-row">
|
<span class="autocomplete-row">
|
||||||
<input name="inst_learned_from" type="text"
|
<input name="inst_learned_from" type="text"
|
||||||
|
|
@ -230,6 +238,7 @@
|
||||||
<tr><td>date_learned</td><td></td><td>YYYY-MM-DD</td></tr>
|
<tr><td>date_learned</td><td></td><td>YYYY-MM-DD</td></tr>
|
||||||
<tr><td>status</td><td></td><td>callable / review / to_learn</td></tr>
|
<tr><td>status</td><td></td><td>callable / review / to_learn</td></tr>
|
||||||
<tr><td>difficulty</td><td></td><td>easy / hard</td></tr>
|
<tr><td>difficulty</td><td></td><td>easy / hard</td></tr>
|
||||||
|
<tr><td>term</td><td></td><td>Created if new</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="csv-format-note">If a tune with the same name already exists a new instrument entry is added to it. Sources, tunings, and musicians are created automatically if they don’t exist.</p>
|
<p class="csv-format-note">If a tune with the same name already exists a new instrument entry is added to it. Sources, tunings, and musicians are created automatically if they don’t exist.</p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue