Fix inline item editing never activating on details pages

The script IIFE was reading window.inlineItemEdit at load time, before
the inline <script> block in @section Scripts had executed to set it.
Config read moved inside DOMContentLoaded so it fires after all inline
scripts in the section have run, regardless of src vs. inline order.
cfg is now passed as a parameter to makeEditable and attachListeners
instead of being captured from the outer IIFE scope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 22:37:27 -04:00
parent eaab0af51f
commit 0e480adbf6
@@ -2,13 +2,11 @@
/// Shared inline-edit behaviour for quote, job, and invoice item rows.
/// Activated when the page sets window.inlineItemEdit = { patchUrl, canEdit, totals }.
/// totals: { subtotal, tax, total, finalPrice, balance } — CSS selectors, any subset.
/// Config is read inside DOMContentLoaded so script load order vs. config assignment doesn't matter.
/// </summary>
(function () {
'use strict';
const cfg = window.inlineItemEdit;
if (!cfg || !cfg.canEdit) return;
function fmt(val) {
return val.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
}
@@ -26,7 +24,7 @@
setTimeout(() => el.remove(), 4000);
}
function updateTotals(data) {
function updateTotals(cfg, data) {
const t = cfg.totals || {};
[
[t.subtotal, data.subtotal],
@@ -41,7 +39,14 @@
});
}
function makeEditable(span) {
function attachListeners(cfg, span) {
span.style.cursor = 'text';
span.title = 'Click to edit';
span.classList.add('inline-editable');
span.addEventListener('click', () => makeEditable(cfg, span), { once: true });
}
function makeEditable(cfg, span) {
const field = span.dataset.inlineField;
const row = span.closest('tr[data-item-id]');
if (!row) return;
@@ -73,7 +78,7 @@
function revert() {
span.innerHTML = savedHTML;
attachListeners(span);
attachListeners(cfg, span);
}
async function commit() {
@@ -136,10 +141,10 @@
if (totalCell) totalCell.textContent = fmt(data.lineTotal);
// Update document-level totals
updateTotals(data);
updateTotals(cfg, data);
// Re-attach click listener for next edit
attachListeners(span);
attachListeners(cfg, span);
} catch {
showError('Could not save &mdash; check your connection and try again.');
@@ -154,14 +159,11 @@
});
}
function attachListeners(span) {
span.style.cursor = 'text';
span.title = 'Click to edit';
span.classList.add('inline-editable');
span.addEventListener('click', () => makeEditable(span), { once: true });
}
// Read config inside DOMContentLoaded — by then the inline <script> that sets
// window.inlineItemEdit has already executed regardless of script load order.
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('[data-inline-field]').forEach(attachListeners);
const cfg = window.inlineItemEdit;
if (!cfg || !cfg.canEdit) return;
document.querySelectorAll('[data-inline-field]').forEach(span => attachListeners(cfg, span));
});
})();