Fix — HTML entities rendering as literal text in JS textContent

textContent treats — as a plain string; replaced with innerHTML
for static dash placeholders, and — JS escape where user input
is concatenated. Also removed a dead textContent line in timeclock-kiosk.js
that was immediately overwritten by innerHTML on the next line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 16:37:37 -04:00
parent 9bbe1e4e27
commit 4f039b8281
7 changed files with 13 additions and 10 deletions
@@ -113,7 +113,7 @@
const preview = document.getElementById('announcementPreview'); const preview = document.getElementById('announcementPreview');
preview.className = 'alert mb-0 ' + (typeMap[type] || 'alert-info'); preview.className = 'alert mb-0 ' + (typeMap[type] || 'alert-info');
document.getElementById('previewTitle').textContent = document.getElementById('Title').value || 'Title'; document.getElementById('previewTitle').textContent = document.getElementById('Title').value || 'Title';
document.getElementById('previewMessage').textContent = ' &mdash; ' + (document.getElementById('Message').value || 'Message'); document.getElementById('previewMessage').textContent = '\u2014' + (document.getElementById('Message').value || 'Message');
} }
document.getElementById('Type')?.addEventListener('change', updatePreview); document.getElementById('Type')?.addEventListener('change', updatePreview);
document.getElementById('Title')?.addEventListener('input', updatePreview); document.getElementById('Title')?.addEventListener('input', updatePreview);
@@ -598,7 +598,7 @@
const modal = bootstrap.Modal.getInstance(document.getElementById('scanReceiptModal')); const modal = bootstrap.Modal.getInstance(document.getElementById('scanReceiptModal'));
if (modal) modal.hide(); if (modal) modal.hide();
statusEl.textContent = 'Scan complete &mdash; review and adjust as needed.'; statusEl.innerHTML = 'Scan complete &mdash; review and adjust as needed.';
} catch (e) { } catch (e) {
statusEl.textContent = 'Error connecting to AI service.'; statusEl.textContent = 'Error connecting to AI service.';
} finally { } finally {
@@ -3382,7 +3382,7 @@
document.getElementById('ovenCalcToggle').addEventListener('click', function (e) { document.getElementById('ovenCalcToggle').addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
const hidden = _calcPanel.classList.toggle('d-none'); const hidden = _calcPanel.classList.toggle('d-none');
if (!hidden) { _calcW.value = ''; _calcD.value = ''; _calcH.value = ''; _calcResult.textContent = '&mdash;'; _calcApply.disabled = true; _calcW.focus(); } if (!hidden) { _calcW.value = ''; _calcD.value = ''; _calcH.value = ''; _calcResult.innerHTML = '&mdash;'; _calcApply.disabled = true; _calcW.focus(); }
}); });
function _updateCalc() { function _updateCalc() {
@@ -3402,7 +3402,7 @@
_calcApply.disabled = false; _calcApply.disabled = false;
_calcApply.dataset.val = val; _calcApply.dataset.val = val;
} else { } else {
_calcResult.textContent = '&mdash;'; _calcResult.innerHTML = '&mdash;';
_calcApply.disabled = true; _calcApply.disabled = true;
} }
} }
@@ -3420,7 +3420,7 @@
document.getElementById('ovenModal').addEventListener('hidden.bs.modal', function () { document.getElementById('ovenModal').addEventListener('hidden.bs.modal', function () {
_calcPanel.classList.add('d-none'); _calcPanel.classList.add('d-none');
_calcW.value = ''; _calcD.value = ''; _calcH.value = ''; _calcW.value = ''; _calcD.value = ''; _calcH.value = '';
_calcResult.textContent = '&mdash;'; _calcApply.disabled = true; _calcResult.innerHTML = '&mdash;'; _calcApply.disabled = true;
}); });
// ───────────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────────
@@ -2936,8 +2936,12 @@
profitEl.className = profit >= 0 ? 'text-success' : 'text-danger'; profitEl.className = profit >= 0 ? 'text-success' : 'text-danger';
document.getElementById('costingMargin').textContent = `${d.grossMargin}%`; document.getElementById('costingMargin').textContent = `${d.grossMargin}%`;
document.getElementById('costingQuotedMargin').textContent = const quotedMarginEl = document.getElementById('costingQuotedMargin');
d.quotedPrice > 0 ? `${d.quotedMargin}% (quoted ${fmt(d.quotedPrice)})` : '&mdash;'; if (d.quotedPrice > 0) {
quotedMarginEl.textContent = `${d.quotedMargin}% (quoted ${fmt(d.quotedPrice)})`;
} else {
quotedMarginEl.innerHTML = '&mdash;';
}
// Powder detail lines // Powder detail lines
const pBody = document.getElementById('powderLines'); const pBody = document.getElementById('powderLines');
@@ -168,7 +168,7 @@
} catch (e) { } catch (e) {
document.getElementById('loadingState').classList.add('d-none'); document.getElementById('loadingState').classList.add('d-none');
document.getElementById('errorMessage').textContent = 'Network error &mdash; please try again.'; document.getElementById('errorMessage').innerHTML = 'Network error &mdash; please try again.';
document.getElementById('errorState').classList.remove('d-none'); document.getElementById('errorState').classList.remove('d-none');
} }
} }
@@ -225,7 +225,7 @@
} catch (e) { } catch (e) {
document.getElementById('loadingState').classList.add('d-none'); document.getElementById('loadingState').classList.add('d-none');
document.getElementById('errorMessage').textContent = 'Network error &mdash; please try again.'; document.getElementById('errorMessage').innerHTML = 'Network error &mdash; please try again.';
document.getElementById('errorState').classList.remove('d-none'); document.getElementById('errorState').classList.remove('d-none');
} }
} }
@@ -123,7 +123,6 @@
var todayLine = result.dailyTotal.toFixed(2) + ' hrs today (' + result.segmentCount + (result.segmentCount === 1 ? ' segment' : ' segments') + ')'; var todayLine = result.dailyTotal.toFixed(2) + ' hrs today (' + result.segmentCount + (result.segmentCount === 1 ? ' segment' : ' segments') + ')';
document.getElementById('tc-confirm-icon').innerHTML = icon; document.getElementById('tc-confirm-icon').innerHTML = icon;
document.getElementById('tc-confirm-title').textContent = result.displayName + ' &mdash; ' + title;
document.getElementById('tc-confirm-title').innerHTML = escHtml(result.displayName) + ' &mdash; ' + title; document.getElementById('tc-confirm-title').innerHTML = escHtml(result.displayName) + ' &mdash; ' + title;
document.getElementById('tc-confirm-time').textContent = timeStr; document.getElementById('tc-confirm-time').textContent = timeStr;
document.getElementById('tc-confirm-today').textContent = todayLine; document.getElementById('tc-confirm-today').textContent = todayLine;